package com.arpa.wms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.arpa.core.web.Result;
import com.arpa.ntocc.common.common.cache.PartyCache;
import com.arpa.ntocc.common.common.cache.PartyGroupCache;
import com.arpa.ntocc.common.common.cache.ShipmentCache;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.CommonUtil;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.ntocc.common.domain.entity.Shipment;
import com.arpa.ntocc.common.domain.enums.CustomerCheckStatusEnum;
import com.arpa.ntocc.common.service.IShipmentService;
import com.arpa.oms.domain.entity.*;
import com.arpa.oms.domain.enums.KuaishouCarrierEnum;
import com.arpa.oms.domain.enums.PurchaseTypeEnum;
import com.arpa.oms.domain.enums.SalePacksOrderStatusEnum;
import com.arpa.oms.domain.enums.SaleTypeEnum;
import com.arpa.oms.domain.vo.OmsPurchaseReturnItemVO;
import com.arpa.oms.domain.vo.OmsPurchaseReturnVO;
import com.arpa.oms.service.*;
import com.arpa.oms.service.impl.*;
import com.arpa.wms.cache.*;
import com.arpa.wms.cancel.delivery.context.DeliveryCancelHandlerContext;
import com.arpa.wms.common.util.DouyinUtil;
import com.arpa.wms.common.util.LogisticsUtils;
import com.arpa.wms.common.util.OrderIdGeneratorUtil;
import com.arpa.wms.common.util.SourcePlatformUtils;
import com.arpa.wms.domain.consts.CommonConst;
import com.arpa.wms.domain.consts.NumConst;
import com.arpa.wms.domain.consts.SqlConst;
import com.arpa.wms.domain.dto.*;
import com.arpa.wms.domain.entity.*;
import com.arpa.wms.domain.enums.*;
import com.arpa.wms.domain.vo.*;
import com.arpa.wms.mapper.CloudprintStandardTemplatesMapper;
import com.arpa.wms.mapper.DeliveryMapper;
import com.arpa.wms.mapper.GoodsUnitConversionMapper;
import com.arpa.wms.mapper.ShopMapper;
import com.arpa.wms.service.*;
import com.arpa.wms.service.IWmsTaskService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.qimen.api.DefaultQimenClient;
import com.qimen.api.QimenClient;
import com.qimen.api.request.DeliveryorderConfirmRequest;
import com.qimen.api.response.DeliveryorderConfirmResponse;
import com.taobao.api.ApiException;
import com.taobao.pac.sdk.cp.PacClient;
import com.taobao.pac.sdk.cp.SendSysParams;
import com.taobao.pac.sdk.cp.dataobject.request.TMS_TRUNK_ORDER_NOTIFY.DeliveryService;
import com.taobao.pac.sdk.cp.dataobject.request.TMS_WAYBILL_GET.*;
import com.taobao.pac.sdk.cp.dataobject.response.TMS_WAYBILL_GET.TmsWaybillGetResponse;
import com.taobao.pac.sdk.cp.dataobject.response.TMS_WAYBILL_GET.WaybillCloudPrintResponse;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.rmi.ServerException;
import java.sql.Struct;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static com.arpa.wms.domain.consts.CommonConst.MAX_ORDER_QUANTITY;
import static com.arpa.wms.domain.consts.NumConst.*;
import static com.arpa.wms.domain.consts.TipConst.*;
import static java.util.stream.Collectors.toList;

/**
 * <p>
 * 出货单 服务实现类
 * </p>
 *
 * @author LEO
 * @since 2020-08-07
 */
@Service
@Log4j2
public class DeliveryServiceImpl extends ServiceImpl<DeliveryMapper, Delivery> implements IDeliveryService {
    private static final BigDecimal DECIMAL100 = new BigDecimal("100");

    public static final String SHIPMENT_CODE = "shipmentCode";
    public static final String SUPPLIER_CODE = "supplierCode";
    public static final String GOOD_CODE = "goodCode";
    public static final String GOODS_CODE = "goodsCode";
    public static final String GOODS_BAR_CODE = "goodsBarCode";
    public static final String PARENT_CODE = "parentCode";
    public static final String CUSTOMER_CODE = "customerCode";
    public static final String TEMPORARY_CODE = "temporaryCode";
    public static final String COUNT = "count";
    public static final String LOGISTICS_CODE = "logisticsCode";
    public static final String PAGE_NUM = "pageNum";
    public static final String PAGE_SIZE = "pageSize";
    public static final String PREPACKAGEDCODE = "prepackagedCode";
    private static final String GET_WAY_BILL_HAS_EXCEPTION_UPDATE_DELIVERY_STATUS_UN_DELIVERD = "【{}】获取面单失败，{}";
    private static final String PLEASE_RETRY_AGAIN = "网络出现异常，请重新获取";


    @Value("${taobao.qimen.url}")
    private String url;
    @Value("${taobao.qimen.appkey}")
    private String appkey;
    @Value("${taobao.qimen.secret}")
    private String secret;
    @Value("${order.confirm.pdd}")
    private String pddSendUrl;
    @Value("${order.confirm.ks}")
    private String ksSendUrl;
    @Value("${order.confirm.tb}")
    private String tbSendUrl;

    @Autowired
    @Lazy
    private ISaleService iSaleService;

    @Autowired
    private ISaleItemService iSaleItemService;

    @Autowired
    @Lazy
    private IOmsAllocationService omsAllocationService;
    @Autowired @Lazy
    private OmsPurchaseReturnServiceImpl omsPurchaseReturnService;
    @Autowired
    private PurchaseItemServiceImpl purchaseItemService;
    @Autowired
    private IOmsInventoryService iOmsInventoryService;
    @Autowired
    private OmsPurchaseReturnItemServiceImpl omsPurchaseReturnItemService;
    @Autowired
    private IOmsInboundItemService iOmsInboundItemService;
    @Autowired
    @Lazy
    private IOmsInboundService iOmsInboundService;
    @Autowired
    @Lazy
    private OmsSettlementServiceImpl omsSettlementService;
    @Autowired
    @Lazy
    private PurchaseServiceImpl purchaseService;
    @Autowired
    @Lazy
    private IShopService shopService;
    @Autowired
    @Lazy
    private ISaleService saleService;
    @Autowired
    @Lazy
    private OrderCancelService orderCancelService;
    @Autowired
    private ShopCache shopCache;

    @Autowired
    private FaceBillBalanceService faceBillBalanceService;

    /**
     * 测试环境请求url
     */
    @Value("${taobao.wly.dailyUrl}")
    private String dailyUrl;

    /**
     * 测试环境appKey
     */
    //@Value("${taobao.wly.dailyAppKey}")
    private String dailyAppKey;

    /**
     * 测试环境secretKey
     */
    //@Value("${taobao.wly.dailySecretKey}")
    private String dailySecretKey;

    /**
     * 测试环境token，token是商家在物流云平台授权ISV后生成的授权码，目前需要商家配置在ISV的软件中
     */
    //@Value("${taobao.wly.dailyToken}")
    private String dailyToken;

    ///**
    // * 线上环境请求url
    // */
    //@Value("${taobao.wly.onlineUrl}")
    //private String onlineUrl;
    //
    ///**
    // * 线上环境appKey，物流云控制台查看
    // */
    //@Value("${taobao.wly.onlineAppKey}")
    //private String onlineAppKey;
    //
    ///**
    // * 线上环境secretKey,物流云控制台查看
    // */
    //@Value("${taobao.wly.onlineSecretKey}")
    //private String onlineSecretKey;
    //
    ///**
    // * 线上环境token，token是商家在物流云平台授权ISV后生成的授权码，目前需要商家配置在ISV的软件中
    // */
    //@Value("${taobao.wly.onlineToken}")
    //private String onlineToken;


    @Autowired
    private GoodsCache goodsCache;

    @Resource
    private GoodsUnitConversionMapper goodsUnitConversionMapper;

    @Autowired
    private PartyGroupCache partyGroupCache;
    @Autowired
    private DeliveryCache deliveryCache;

    @Autowired
    @Lazy
    private IGoodsService iGoodsService;


    @Autowired
    private SupplierCache supplierCache;


    private final PartyCache partyCache;

    private final ShipmentCache shipmentCache;

    private final WarehouseCache warehouseCache;

    private final CustomerCache customerCache;

    private final IShipmentService iShipmentService;

    private final IWarehouseShipmentService iWarehouseShipmentService;

    private final ICustomerService iCustomerService;

    private final IWarehouseService iWarehouseService;

    private final IGoodsUnitService iGoodsUnitService;


    @Autowired
    private IDeliveryItemService iDeliveryItemService;

    @Autowired
    @Lazy
    private IDeliveryDistributionService iDeliveryDistributionService;

    @Autowired
    @Lazy
    private IPickingService iPickingService;


    @Autowired
    private IPickingItemService iPickingItemService;

    @Autowired
    private IInventoryService iInventoryService;

    @Autowired
    private IInventoryRecordService iInventoryRecordService;

    @Autowired
    private IPickingRegisterService iPickingRegisterService;


    @Autowired
    @Lazy
    private IWmsTaskService iWmsTaskService;

    @Autowired
    @Lazy
    private ICollectService iCollectService;

    @Autowired
    @Lazy
    private ICollectItemService iCollectItemService;

    @Autowired
    @Lazy
    private IDamagedService iDamagedService;

    @Autowired
    @Lazy
    private IDamagedItemService iDamagedItemService;


    @Autowired
    @Lazy
    private IReceiveItemService iReceiveItemService;


    @Autowired
    @Lazy
    private IReceiveService iReceiveService;


    @Autowired
    @Lazy
    private IAllocationService iAllocationService;

    @Autowired
    @Lazy
    private IAllocationItemService iAllocationItemService;


    @Autowired
    @Lazy
    private IOutboundService iOutboundService;
    @Resource
    private CloudprintStandardTemplatesMapper cloudprintStandardTemplatesMapper;

    @Resource
    private CarrierCache carrierCache;

    @Autowired
    private IGoodsPnService goodsPnService;
    @Autowired
    private ICarrierService carrierService;

    @Autowired
    private ISalePacksService iSalePacksService;

    @Resource
    private ShopMapper shopMapper;
    @Autowired
    private BatchRuleCache batchRuleCache;

    public DeliveryServiceImpl(PartyCache partyCache, WarehouseCache warehouseCache,
                               CustomerCache customerCache,
                               IShipmentService iShipmentService,
                               IWarehouseShipmentService iWarehouseShipmentService,
                               ICustomerService iCustomerService,
                               IWarehouseService iWarehouseService,
                               IGoodsUnitService iGoodsUnitService,
                               ShipmentCache shipmentCache) {
        this.partyCache = partyCache;
        this.warehouseCache = warehouseCache;
        this.customerCache = customerCache;
        this.iShipmentService = iShipmentService;
        this.iWarehouseShipmentService = iWarehouseShipmentService;
        this.iCustomerService = iCustomerService;
        this.iWarehouseService = iWarehouseService;
        this.iGoodsUnitService = iGoodsUnitService;
        this.shipmentCache = shipmentCache;
    }

    private final static String ACTUAL_QUANTITY_EQUALS_ACTUAL_QUANTIT = " actual_quantity = actual_quantity + ";
    private final static String REFUND_QUANTITY_EQUALS_REFUND_QUANTIT = " refund_quantity = refund_quantity + ";
    private final static String REFUND_BASIC_QUANTITY_EQUALS_REFUND_BASIC_QUANTIT = " refund_basic_quantity = refund_basic_quantity + ";

    /**
     * 分配确认
     *
     * @param code
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result confirm(String code,String groupCode) {
        // 判断单据状态
        Delivery dbEntity = this.getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, code).eq(Delivery::getGroupCode,groupCode));
        if (dbEntity == null) {
            throw new ServiceException("发货单信息不存在");
        }
        if (!StrUtil.containsAny(dbEntity.getStatus(), DeliveryStatusEnum.PART_DISTRIBUTION.getValue(), DeliveryStatusEnum.UN_DISTRIBUTION.getValue())) {
            throw new ServiceException("当前发货单状态无法进行分配完成操作");
        }
        // 获取未生成拣货单的分配信息
        iDeliveryDistributionService.queryWithItemList(code, StrUtil.EMPTY);

        // 判断有未生成拣货单分配信息或单据是部分分配状态， 或判断分配数量
        if (dbEntity.getDistributionNum().compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("没有分配信息，无法进行分配完成操作");
        }
        // 根据未生成拣货单的分配信息生成上架单  XYTODO TODO 默认不再自动生成拣货单了
        /*if (IterUtil.isNotEmpty(itemWithDistributions)) {
            savePickingAndUpdateDeliveryInfo(dbEntity, itemWithDistributions);
        }*/
        // 更新发货单状态为分配完成
        dbEntity.setStatus(DeliveryStatusEnum.DISTRIBUTIONED.getValue());
        this.updateByCode(dbEntity);

        // 如果所有出库单也已完成，则需要触发部分单据回填机制
        if (isDeliveryAndOutboundAllFinish(dbEntity.getCode(),groupCode)) {
            afterConfirmWithAllOutboundFinish(dbEntity);
        }

        return Result.ok();
    }

    /**
     * 发货单“完全确认”后并且所有出库单也已完成， 此时需要处理部分单据的进一步生成其他单据的流程
     * 如调拨单生成调入仓的调拨单，oms采退单需要调整oms采购单退货数量，wms发货退货单需要调整wms收货单退货数量
     *
     * @param delivery
     */
    @Transactional(rollbackFor = {Exception.class})
    public void afterConfirmWithAllOutboundFinish(Delivery delivery) {
        DeliveryTypeEnum deliveryTypeEnum = DeliveryTypeEnum.getEnumByValue(delivery.getType());
        switch (deliveryTypeEnum) {
            // 调拨单回填
            case ALLOCATION:
                //omsAllocationService.afterWmsFinishAllOutbound(delivery.getSourceCode());
                break;
            case PURCHASE:
                if (NUM_ONE.equals(delivery.getIsOmsOrder())) {
                    // oms采退单
                    //adjustPurchaseRefundQuantity(delivery.getSourceCode());
                } else {
                    // wms发货退货单
                    List<DeliveryItem> deliveryItems = iDeliveryItemService.list(new QueryWrapper<DeliveryItem>().lambda()
                            .eq(DeliveryItem::getDeliveryCode, delivery.getCode())
                            .eq(DeliveryItem::getGroupCode, delivery.getGroupCode()));
                    updateSourceQuantityForPurchase(delivery, deliveryItems);
                }
                break;
            default:
                break;
        }
    }

    /**
     * 根据发货单生成拣货单
     *
     * @param delivery
     * @return
     */
    public Picking generatePicking(Delivery delivery) {
        Picking picking = new Picking();
        BeanUtil.copyProperties(delivery, picking);
        picking.setCode(OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.PK, delivery.getShipmentCode(),UserUtil.getBranchCode()));
        picking.setStockoutQuantity(BigDecimal.ZERO);
        picking.setNotPickingQuantity(delivery.getDistributionNum());
        picking.setDeliveryCode(delivery.getCode());
        picking.setPlanQuantity(delivery.getDistributionNum());
        picking.setStatus(PickingStatusEnum.NOT_PICK.getValue());
        picking.setOrderTag(OrderTagEnum.QUANTITY.getValue());
        picking.setReview(NUM_ONE);
        picking.setCarrierCode(delivery.getCarrierCode());
        picking.setCarrierName(delivery.getCarrierName());
        if (StrUtil.isNotBlank(delivery.getRemarks())) {
            picking.setRemarks(delivery.getRemarks());
        } else {

            picking.setRemarks("");
        }
        return picking;
    }

    /**
     * 根据拣货单、分配明细、出货分配信息生成拣货明细
     *
     * @param picking
     * @param deliveryItemWithDistributionVOS
     * @return
     */
    public List<PickingItem> generatePickingItems(Picking picking, List<DeliveryItemWithDistributionVO> deliveryItemWithDistributionVOS) {
        List<PickingItem> pickingItems = new ArrayList<>();
        List<PickingRegister> pickingRegisters = new ArrayList<>();
        PickingItem pickingItem;
        PickingRegister pickingRegister;

        for (DeliveryItemWithDistributionVO temp : deliveryItemWithDistributionVOS) {
            // 分配数量大于0 才生成拣货明细
            if (temp.getDistributionQuantity() != null && temp.getDistributionQuantity().compareTo(BigDecimal.ZERO) > 0) {
                pickingItem = new PickingItem();
                BeanUtil.copyProperties(temp, pickingItem);
                pickingItem.setCode(IdUtil.simpleUUID());
                pickingItem.setCreatedBy(UserUtil.getCode());
                pickingItem.setPickingCode(picking.getCode());
                pickingItem.setDeliveryDistributionCode(temp.getCode());
                // 使用商品基本单位
                pickingItem.setGoodsUnit(temp.getBasicUnit());
                pickingItem.setStockoutQuantity(BigDecimal.ZERO);
                // 待拣货状态
                pickingItem.setStatus(PickingStatusEnum.NOT_PICK.getValue());

                pickingItems.add(pickingItem);

                // 生成一条未收货的拣货确认单
                pickingRegister = new PickingRegister();
                BeanUtil.copyProperties(temp, pickingRegister);
                pickingRegister.setCode(IdUtil.simpleUUID());
                pickingRegister.setPickingItemCode(pickingItem.getCode());
                pickingRegister.setCreatedBy(UserUtil.getCode());
                pickingRegister.setPickingCode(picking.getCode());
                pickingRegister.setDeliveryDistributionCode(temp.getCode());
                // 未收货数量初始值为分配数量
                pickingRegister.setNotPickingQuantity(pickingItem.getDistributionQuantity());
                // 拣货数量初始值也是分配数量，冗余字段，用于与其他确认单计算时统一字段
                pickingRegister.setPickingQuantity(pickingItem.getDistributionQuantity());
                // 使用商品基本单位
                pickingRegister.setGoodsUnit(temp.getBasicUnit());
                // 确认单状态为未拣货状态
                pickingRegister.setStatus(PickingRegisterStatusEnum.NOT_PICK.getValue());
                pickingRegisters.add(pickingRegister);
            }

        }

        // 保存初始默认的拣货确认单
        iPickingRegisterService.saveBatch(pickingRegisters);

        return pickingItems;
    }


    /**
     * 根据拣货单、分配明细、出货分配信息生成拣货明细
     *
     * @param picking
     * @param distributions
     * @param distributions
     * @return
     */
    private List<PickingItem> generatePickingItemsByDistributions(Picking picking, List<DeliveryDistribution> distributions, List<DeliveryItem> deliveryItemList, String deliveryCode) {
        String createdBy = UserUtil.getCode();
        String createdName = partyCache.translate(createdBy);
        LocalDateTime nowTime = LocalDateTime.now();
        List<PickingItem> pickingItems = new ArrayList<>();
        PickingItem pickingItem;
        Map<String, DeliveryItem> deliveryItemMap = deliveryItemList.stream().collect(Collectors.toMap(DeliveryItem::getCode, e -> e));
        distributions = distributions.stream().filter(temp -> temp.getDistributionQuantity() != null && temp.getDistributionQuantity().compareTo(BigDecimal.ZERO) > 0).collect(toList());

        for (DeliveryDistribution temp : distributions) {
            // 分配数量大于0 才生成拣货明细
            pickingItem = new PickingItem();
            BeanUtil.copyProperties(temp, pickingItem);
            pickingItem.setCode(IdUtil.simpleUUID());
            pickingItem.setCreatedBy(createdBy);
            pickingItem.setCreatedName(createdName);
            pickingItem.setGmtCreated(nowTime);
            pickingItem.setModifiedBy(createdBy);
            pickingItem.setModifiedName(createdName);
            pickingItem.setGmtModified(nowTime);
            pickingItem.setPickingCode(picking.getCode());
            pickingItem.setWavePickingCode(picking.getWavePickingCode());
            pickingItem.setDeliveryDistributionCode(temp.getCode());
            // 使用商品基本单位
            if (StrUtil.isNotEmpty(temp.getBasicUnit())) {
                pickingItem.setGoodsUnit(temp.getBasicUnit());
            }
            if (StrUtil.isNotEmpty(temp.getBasicUnitName())) {
                pickingItem.setGoodsUnitName(temp.getGoodsUnitName());
            }
            pickingItem.setStockoutQuantity(BigDecimal.ZERO);
            // 待拣货状态
            pickingItem.setStatus(PickingStatusEnum.NOT_PICK.getValue());
            DeliveryItem deliveryItem = deliveryItemMap.get(temp.getDeliveryItemCode());
            pickingItem.setBasicQuantity(deliveryItem.getBasicQuantity());
            pickingItem.setSourceItemCode(deliveryItem.getSourceItemCode());

            pickingItems.add(pickingItem);

        }

        return pickingItems;
    }


    /**
     * 保存
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean save(Delivery entity) {
        if (StringUtils.isBlank(entity.getCode())) {
            entity.setCode(IdUtil.simpleUUID());
        }
        entity.setGroupCode(UserUtil.getBranchCode());
        if (StringUtils.isBlank(entity.getCreatedBy())) {
            entity.setCreatedBy(UserUtil.getCode());
            entity.setCreatedName(partyCache.translate(UserUtil.getCode()));
        }
        return super.save(entity);
    }

    /**
     * 根据code更新实体
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int updateByCode(Delivery entity) {
        entity.setModifiedBy(UserUtil.getCode());
        entity.setModifiedName(partyCache.translate(UserUtil.getCode()));
        return baseMapper.update(entity, new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, entity.getCode()).eq(Delivery::getGroupCode,entity.getGroupCode()));
    }

    /**
     * 查询列表
     *
     * @param deliveryDTO
     * @return
     */
    @Override
    public List<DeliveryVO> queryList(DeliveryDTO deliveryDTO) {
        //排序字段名需要驼峰转数据库下划线类型字段名
        if (StringUtils.isNotEmpty(deliveryDTO.getSortField())) {
            deliveryDTO.setSortField(CommonUtil.camel2Underline(deliveryDTO.getSortField()));
        }
        deliveryDTO.setGroupCode(UserUtil.getBranchCode());
        List<DeliveryVO> deliveryVOS;
        // 如果商品查询字段不为空，则需要关联明细表查询订单
        if (StrUtil.isNotBlank(deliveryDTO.getNormalGoodCodes())) {
            deliveryVOS = this.queryListJoinItem(deliveryDTO);
        } else {
            deliveryVOS = baseMapper.queryList(deliveryDTO);
        }

        return deliveryVOS.stream().peek(info -> {
            DeliveryStatusEnum enumByCode = DeliveryStatusEnum.getEnumByValue(info.getStatus());
            if (null != enumByCode) {
                info.setStatusText(enumByCode.getDesc());
            }
            DeliveryTypeEnum billType = DeliveryTypeEnum.getEnumByValue(info.getType());
            if (null != billType) {
                info.setTypeText(billType.getDesc());
            }
            String logisticsName = LogisticsUtils.translate(info.getLogisticsCode());
            if (StrUtil.isEmpty(logisticsName)) {
                info.setLogisticsName(logisticsName);
            }
            String carrierTypeName = CarrierTypeEnum.translate(info.getCarrierType());
            if (StrUtil.isNotEmpty(carrierTypeName)) {
                info.setCarrierTypeName(carrierTypeName);
            }
            // 翻译订单来源类型
            info.setSourceTypeText(DeliverySourceTypeEnum.translate(info.getSourceType()));
            //翻译订单来源平台名称
            info.setSourcePlatformName(SourcePlatformUtils.translate(info.getSourcePlatformCode()));

            info.setWarehouseName(warehouseCache.translate(info.getWarehouseCode()));
        }).collect(Collectors.toList());
    }


    /**
     * 订单导出
     * @param deliveryDTO
     * @return
     */
    @Override
    public  List<DeliveryVO> importDelivery(DeliveryDTO deliveryDTO) {

        List<DeliveryVO> deliveryVOList =  baseMapper.importDelivery(deliveryDTO);
        if (IterUtil.isEmpty(deliveryVOList)) {
            return Lists.newArrayList();
        }

        //通过承运商code获取店铺code,在获取店铺的手机号
        deliveryVOList.forEach(e->{
            e.setSku(e.getGoodsSortQuantity());
            e.setType(DeliveryTypeEnum.translate(e.getType()));
            //不要店铺的电话号码
           /* Carrier carrier = carrierCache.getCarrier(e.getCarrierCode());
            if (null != carrier) {
                Shop shop = shopCache.getShop(carrier.getShopCode());
                if (null != shop) {
                    e.setShopNickPhone(shop.getPhone());
                }
            }*/
        });

        return deliveryVOList;
    }

    /**
     * 查询导出数据合计,包含总数
     * @param deliveryDTO
     * @return
     */
    @Override
    public DeliveryVO importDeliveryTotal(DeliveryDTO deliveryDTO){
        return this.baseMapper.importDeliveryTotal(deliveryDTO);
    }

    /**
     * 重构入参
     * @param deliveryDTO
     * @return
     */
    @Override
    public DeliveryDTO constructQueryParams(DeliveryDTO deliveryDTO){
        //不要店铺的手机号
        /*if (StrUtil.isNotEmpty(deliveryDTO.getShopNickPhone())) {
            List<String> shopNicks = shopService.list(new LambdaQueryWrapper<Shop>().select(Shop::getName)
                    .eq(Shop::getPhone, deliveryDTO.getShopNickPhone())).stream().map(Shop::getName).collect(Collectors.toList());
            if (IterUtil.isNotEmpty(shopNicks)) {
                deliveryDTO.setShopNicks(shopNicks);
            }
        }*/
        //切换字段查询，出现sku的原因是导出功能已经用了这个字段了，为了保持兼容性，这里做了替换
        deliveryDTO.setGoodsSortQuantityStart(deliveryDTO.getSkuFloor());
        deliveryDTO.setGoodsSortQuantityEnd(deliveryDTO.getSkuCeiling());
        deliveryDTO.setTotalNumStart(deliveryDTO.getTotalNumFloor());
        deliveryDTO.setTotalNumEnd(deliveryDTO.getTotalNumCeiling());
        return deliveryDTO;

    }


    /**
     * 查询合计,包含总数
     *
     * @param deliveryDTO
     * @return
     */
    @Override
    public DeliveryVO queryListSum(DeliveryDTO deliveryDTO) {
        if (IterUtil.isEmpty(deliveryDTO.getShipmentCodes())) {
            return null;
        }

        StringBuilder sb = new StringBuilder();
        sb.append("SELECT ");
        deliveryDTO.getShipmentCodes().forEach(e -> {
            sb.append("(SELECT count(*) AS total FROM `wms_delivery` a  WHERE 1=1 ");
            if (ObjectUtil.isNotEmpty(deliveryDTO.getIsCancel())) {
                sb.append(" and `is_cancel` = ").append(deliveryDTO.getIsCancel());
            }
            sb.append(" and `is_deleted` =  0 ");
            if (StrUtil.isNotEmpty(deliveryDTO.getWarehouseCode())) {
                sb.append(" and `warehouse_code` ='").append(deliveryDTO.getWarehouseCode()).append("' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getGroupCode())) {
                sb.append(" and `group_code` ='").append(deliveryDTO.getGroupCode()).append("' ");
            }
            sb.append(" and `shipment_code` =  '").append(e).append("' ");


            if (StrUtil.isNotEmpty(deliveryDTO.getCode())) {
                sb.append("  and `code` like'%").append(deliveryDTO.getCode()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getSourceCode())) {
                sb.append(" and `source_code` ='").append(deliveryDTO.getSourceCode()).append("' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getShipmentName())) {
                sb.append("  and `shipment_name` like'%").append(deliveryDTO.getShipmentName()).append("%' ");
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getType())) {
                sb.append("  and `type` ='").append(deliveryDTO.getType()).append("' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getShopNick())) {
                sb.append(" and `shop_nick` like'%").append(deliveryDTO.getShopNick()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getIsvCode())) {
                sb.append(" and `isv_code` like'%").append(deliveryDTO.getIsvCode()).append("%' ");
            }

            if (StrUtil.isNotEmpty(deliveryDTO.getDeliveryOrderCode())) {
                sb.append(" and `delivery_order_code` like'%").append(deliveryDTO.getDeliveryOrderCode()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getLogisticsCode())) {
                sb.append(" and `logistics_code` like'%").append(deliveryDTO.getLogisticsCode()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getWaybillCode())) {
                sb.append(" and `waybill_code` like'%").append(deliveryDTO.getWaybillCode()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getWavePickingCode())) {
                if (deliveryDTO.isWavePickingCodeFullQuery()){
                    sb.append(" and `wave_picking_code` ='").append(deliveryDTO.getWavePickingCode()).append("' ");
                }else {
                    sb.append(" and `wave_picking_code` like'%").append(deliveryDTO.getWavePickingCode()).append("%' ");
                }
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getCarrierName())) {
                sb.append(" and `carrier_name` like'%").append(deliveryDTO.getCarrierName()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getCarrierCode())) {
                sb.append(" and `carrier_code` ='").append(deliveryDTO.getCarrierCode()).append("' ");
            }


            if (ObjectUtil.isNotEmpty(deliveryDTO.getStatus())) {
                sb.append(" and `status` ='").append(deliveryDTO.getStatus()).append("' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getRemarks())) {
                sb.append(" and `remarks` ='").append(deliveryDTO.getRemarks()).append("' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getId())) {
                sb.append(" and `id` =").append(deliveryDTO.getId());
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getCreatedBy())) {
                sb.append(" and `created_by` ='").append(deliveryDTO.getCreatedBy()).append("' ");
            }

            if (ObjectUtil.isNotEmpty(deliveryDTO.getGmtCreated())) {
                sb.append(" and `gmt_created` ='").append(deliveryDTO.getGmtCreated()).append("' ");
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getModifiedBy())) {
                sb.append(" and `modified_by` ='").append(deliveryDTO.getModifiedBy()).append("' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getModifiedName())) {
                sb.append(" and `modified_name` ='").append(deliveryDTO.getModifiedName()).append("' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getCreatedName())) {
                sb.append(" and `created_name` ='").append(deliveryDTO.getCreatedName()).append("' ");
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getGoodsSortQuantityStart()) && ObjectUtil.isNotEmpty(deliveryDTO.getGoodsSortQuantityEnd())) {
                sb.append(" and `goods_sort_quantity` between ").append(deliveryDTO.getGoodsSortQuantityStart()).append(" and ").append(deliveryDTO.getGoodsSortQuantityEnd());
            }

            if (ObjectUtil.isNotEmpty(deliveryDTO.getGmtCreatedStart()) && ObjectUtil.isNotEmpty(deliveryDTO.getGmtCreatedEnd())) {
                sb.append(" and `gmt_created` between '").append(deliveryDTO.getGmtCreatedStart()).append("' and '").append(deliveryDTO.getGmtCreatedEnd()).append("' ");
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getGmtModifiedStart()) && ObjectUtil.isNotEmpty(deliveryDTO.getGmtModifiedEnd())) {
                sb.append(" and `gmt_modified` between '").append(deliveryDTO.getGmtModifiedStart()).append("' and '").append(deliveryDTO.getGmtModifiedEnd()).append("' ");
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getOutboundCompletionTimeStart()) && ObjectUtil.isNotEmpty(deliveryDTO.getOutboundCompletionTimeEnd())) {
                sb.append(" and `outbound_completion_time` between '").append(deliveryDTO.getOutboundCompletionTimeStart()).append("' and '").append(deliveryDTO.getOutboundCompletionTimeEnd()).append("' ");
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getTotalNumStart()) && ObjectUtil.isNotEmpty(deliveryDTO.getTotalNumEnd())) {
                sb.append(" and `total_num` between '").append(deliveryDTO.getTotalNumStart()).append("' and '").append(deliveryDTO.getTotalNumEnd()).append("' ");
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getDistributionNumStart()) && ObjectUtil.isNotEmpty(deliveryDTO.getDistributionNumEnd())) {
                sb.append(" and `distribution_num` between '").append(deliveryDTO.getDistributionNumStart()).append("' and '").append(deliveryDTO.getDistributionNumEnd()).append("' ");
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getPickNumStart()) && ObjectUtil.isNotEmpty(deliveryDTO.getPickNumEnd())) {
                sb.append(" and `pick_num` between  ").append(deliveryDTO.getPickNumStart()).append("  and  ").append(deliveryDTO.getPickNumEnd());
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getSendNumStart()) && ObjectUtil.isNotEmpty(deliveryDTO.getSendNumEnd())) {
                sb.append(" and `send_num` between  ").append(deliveryDTO.getSendNumStart()).append("  and  ").append(deliveryDTO.getSendNumEnd());
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getPickNum())) {
                sb.append(" and `pick_num` ='").append(deliveryDTO.getPickNum());
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getSendNum())) {
                sb.append(" and `send_num` ='").append(deliveryDTO.getSendNum());
            }

            if (StrUtil.isNotEmpty(deliveryDTO.getOrderTag())) {
                sb.append(" and `order_tag` ='").append(deliveryDTO.getOrderTag()).append("' ");
            }

            if (StrUtil.isNotEmpty(deliveryDTO.getReceiverName())) {
                sb.append(" and `receiver_name` like'%").append(deliveryDTO.getReceiverName()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getReceiverMobile())) {
                sb.append(" and `receiver_mobile` like'%").append(deliveryDTO.getReceiverMobile()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getSenderDetailAddress())) {
                sb.append(" and `sender_detail_address` like'%").append(deliveryDTO.getSenderDetailAddress()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getReceiverDetailAddress())) {
                sb.append("  and CONCAT(IFNULL(receiver_province,''),IFNULL(receiver_city,''),IFNULL(receiver_district,''),IFNULL(receiver_detail_address,'')) LIKE '").append(deliveryDTO.getReceiverDetailAddress()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getShipmentName())) {
                sb.append("  and `shipment_name` like'%").append(deliveryDTO.getShipmentName()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getCarrierType())) {
                sb.append("  and `carrier_type` like'%").append(deliveryDTO.getCarrierType()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getSourceType())) {
                sb.append("  and `source_type` ='").append(deliveryDTO.getSourceType()).append("' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getRemarks())) {
                sb.append("  and `remarks` ='").append(deliveryDTO.getRemarks()).append("' ");
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getVersion())) {
                sb.append(" and `version` = ").append(deliveryDTO.getVersion());
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getWaybillPrintCount())) {
                sb.append(" and `waybill_print_count` = ").append(deliveryDTO.getWaybillPrintCount());
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getId())) {
                sb.append("  and `id` = ").append(deliveryDTO.getId());
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getCreatedBy())) {
                sb.append("  and `created_by` ='").append(deliveryDTO.getCreatedBy()).append("' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getWaveSql())) {
                sb.append(deliveryDTO.getWaveSql());
            }
            sb.append(" ) +");
        });
        sb.replace(sb.length() - 1, sb.length(), "");
        sb.append(" AS total ");

        return this.baseMapper.queryListSum(sb.toString());
    }

    /**
     * 查询合计,包含总数
     * @param deliveryDTO
     * @return
     */
    @Override
    public DeliveryVO queryListTotal(DeliveryDTO deliveryDTO){
        // 如果商品查询字段不为空，则需要关联明细表查询订单
        if (StrUtil.isNotBlank(deliveryDTO.getNormalGoodCodes())) {
            return queryListJoinItemSum(deliveryDTO);
        }
        return this.baseMapper.queryListTotal(deliveryDTO);

    }

    /**
     * 查询列表
     *
     * @param deliveryDTO
     * @return
     */
    @Override
    public List<DeliveryVO> queryListJoinItem(DeliveryDTO deliveryDTO) {
        deliveryDTO.setGroupCode(UserUtil.getBranchCode());
        //排序字段名需要驼峰转数据库下划线类型字段名
        if (StringUtils.isNotEmpty(deliveryDTO.getSortField())) {
            deliveryDTO.setSortField(CommonUtil.camel2Underline(deliveryDTO.getSortField()));
        }
        List<DeliveryVO> deliveryVOS = baseMapper.queryListJoinItem(deliveryDTO);
        return deliveryVOS.stream().peek(info -> {
            DeliveryStatusEnum enumByCode = DeliveryStatusEnum.getEnumByValue(info.getStatus());
            if (null != enumByCode) {
                info.setStatusText(enumByCode.getDesc());
            }
            DeliveryTypeEnum billType = DeliveryTypeEnum.getEnumByValue(info.getType());
            if (null != billType) {
                info.setTypeText(billType.getDesc());
            }
            String logisticsName = LogisticsUtils.translate(info.getLogisticsCode());
            if (StrUtil.isEmpty(logisticsName)) {
                info.setLogisticsName(logisticsName);
            }
            // 翻译订单来源类型
            info.setSourceTypeText(DeliverySourceTypeEnum.translate(info.getSourceType()));

            info.setWarehouseName(warehouseCache.translate(info.getWarehouseCode()));
        }).collect(Collectors.toList());
    }

    @Override
    public List<DeliveryVO> queryWaveListJoinItem(DeliveryDTO deliveryDTO) {
        deliveryDTO.setGroupCode(UserUtil.getBranchCode());
        //排序字段名需要驼峰转数据库下划线类型字段名
        if (StringUtils.isNotEmpty(deliveryDTO.getSortField())) {
            deliveryDTO.setSortField(CommonUtil.camel2Underline(deliveryDTO.getSortField()));
        }
        List<DeliveryVO> deliveryVOS = baseMapper.queryWaveListJoinItem(deliveryDTO);
        return deliveryVOS.stream().peek(info -> {
            DeliveryStatusEnum enumByCode = DeliveryStatusEnum.getEnumByValue(info.getStatus());
            if (null != enumByCode) {
                info.setStatusText(enumByCode.getDesc());
            }
            DeliveryTypeEnum billType = DeliveryTypeEnum.getEnumByValue(info.getType());
            if (null != billType) {
                info.setTypeText(billType.getDesc());
            }
            String logisticsName = LogisticsUtils.translate(info.getLogisticsCode());
            if (StrUtil.isEmpty(logisticsName)) {
                info.setLogisticsName(logisticsName);
            }
            // 翻译订单来源类型
            info.setSourceTypeText(DeliverySourceTypeEnum.translate(info.getSourceType()));

            info.setWarehouseName(warehouseCache.translate(info.getWarehouseCode()));
        }).collect(Collectors.toList());
    }


    /**
     * 查询合计,包含总数
     *
     * @param deliveryDTO
     * @return
     */
    @Override
    public DeliveryVO queryListJoinItemSum(DeliveryDTO deliveryDTO) {
        deliveryDTO.setGroupCode(UserUtil.getBranchCode());
        DeliveryVO deliveryVO = baseMapper.queryListJoinItemSum(deliveryDTO);
        return deliveryVO;
    }

    /**
     * 查询合计,包含总数
     *
     * @param deliveryDTO
     * @return
     */
    @Override
    public DeliveryVO queryWaveListJoinItemSum(DeliveryDTO deliveryDTO) {
        deliveryDTO.setGroupCode(UserUtil.getBranchCode());
        DeliveryVO deliveryVO = baseMapper.queryWaveListJoinItemSum(deliveryDTO);
        return deliveryVO;
    }


    /**
     * 判断是整数还是小数
     *
     * @param number
     * @return
     */
    public static boolean compareNumber(BigDecimal number) {
        if (!"".equals(number) && number != null) {
            if (new BigDecimal(number.intValue()).compareTo(number) == 0) {
                //整数
                return true;
            } else {
                //小数
                return false;
            }
        }
        return false;
    }

    public static boolean doubleyn(String str, int dousize) {
        try {
            double num = Double.valueOf(str);//把字符串强制转换为数字
            if (str.trim().indexOf(".") == -1) {
                return false;
            }
            int fourplace = str.trim().length() - str.trim().indexOf(".") - 1;
            if (fourplace > dousize) {
                throw new ServiceException("数量小数不能大于6位!");
            } else {
                return true;
            }
        } catch (Exception e) {
            throw new ServiceException("存在重复的商品信息，请确认");
        }
    }


    /**
     * 保存发货单
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result saveWithGoods(Delivery entity) {
        String groupCode = entity.getGroupCode();
        if (ObjectUtil.isNull(entity) || IterUtil.isEmpty(entity.getDeliveryItemList())) {
            return Result.error("提交的数据为空，请确认");
        }
        if (StrUtil.isEmpty(entity.getShipmentName())) {
            entity.setShipmentName(shipmentCache.translate(entity.getShipmentCode()));
        }
        List<DeliveryItem> deliveryItems = entity.getDeliveryItemList();
        // 校验是否有重复项
        Map<String, Object> validDuplication = new HashMap<>();
        String validKey;
        //主单体积
        BigDecimal volume = BigDecimal.ZERO;
        for (DeliveryItem e : deliveryItems) {
            validKey = e.getGoodsCode() + "-" + e.getGoodsUnit();
            if (validDuplication.containsKey(validKey)) {
                throw new ServiceException("存在重复的商品信息，请确认");
            } else {
                validDuplication.put(validKey, e);
            }
            //判断用户输入的数量个数，如果是小数必须是6位
            BigDecimal planQuantity = e.getQuantity();
            boolean b = compareNumber(planQuantity);
            if (!b) {
                doubleyn(String.valueOf(planQuantity), 6);
            }
            //汇总商品的体积
            BigDecimal volumeItem = e.getVolume() != null ? e.getVolume() : BigDecimal.ZERO;
            volume = volume.add(volumeItem);
        }
        //todo 新增的订单是否需要获取运单号，如果需要，则将下面的注释打开，然后再维护delivery_order_code字段即可
        //新增承运商发货地址等信息
        //updateDeliverySenderInfo(entity);

        // 用于判断是新增或修改
        boolean isCreated = false;
        entity.setTotalNum(getTotalNum(entity));
        // 出库单号，来源单号的生成规则
        if (StrUtil.isBlank(entity.getCode())) {
            String code = OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.FH, entity.getShipmentCode(),groupCode);
            entity.setCode(code);
            isCreated = true;
        }
        String createdBy = UserUtil.getCode();
        String createdName = partyCache.translate(createdBy);
        //当前操作人
        entity.setCreatedBy(createdBy);
        entity.setCreatedName(createdName);
        entity.setModifiedBy(createdBy);
        entity.setModifiedName(createdName);
        entity.setVolume(volume);

        //默认包裹数为1
        entity.setPackageNum(NUM_ONE);

        //单据类型
        if (StrUtil.isEmpty(entity.getType())) {
            entity.setType(DeliveryTypeEnum.OUT_SALE.getValue());
        }
        entity.setGroupCode(groupCode);

        // 用于计算商品种数
        Map<String, Boolean> goodsSortMap = new HashMap<>();
        // 明细增加必要项
        deliveryItems.forEach(info -> {
            info.setCode(IdUtil.simpleUUID());
            info.setDeliveryCode(entity.getCode());
            info.setCreatedBy(createdBy);
            info.setDeleted(0);
            info.setTotalNum(info.getQuantity().multiply(info.getBasicQuantity()));
            info.setPlanQuantity(info.getTotalNum());
            info.setGroupCode(groupCode);
            goodsSortMap.put(info.getGoodsCode(), true);
        });

        entity.setGoodsSortQuantity(new BigDecimal(Integer.toString(goodsSortMap.size())));
        if (entity.getGoodsSortQuantity().compareTo(BigDecimal.ONE) > NUM_ZERO) {
            entity.setOrderTag("多品");
        } else {
            entity.setOrderTag("单品");
        }
        //缺货是否发货标识，默认为不发货
        if (null == entity.getIsDeliveringIfStockout()) {
            entity.setIsDeliveringIfStockout(NUM_ZERO);
        }
        Carrier carrier = carrierCache.getCarrier(entity.getCarrierCode());
        //承运商信息校验，即选择了承运商，应该给提示收货人信息必填
        validateReceiveInfo(entity);
        if (null != carrier) {

            /**
             * 发件人名称
             */
            String senderName = carrier.getSenderName();

            /**
             * 发件人移动电话
             */
            String senderMobile = carrier.getSenderMobile();

            /**
             * 发件人省份
             */
            String senderProvince = carrier.getProvince();

            /**
             * 发件人城市
             */
            String senderCity = carrier.getCity();

            /**
             * 发件人区县
             */
            String senderDistrict = carrier.getCounty();
            String sourcePlatformCode = carrier.getPlatformType();

            String sendAddressStr = carrier.getAddress();
            // 承运商类型
            String carrierType = carrier.getCarrierType();
            // 承运商名称
            String carrierName = carrier.getName();
            String templateAddress = carrier.getTemplateAddress();

            String logisticsCode = carrier.getOuterCode();
            entity.setLogisticsCode(logisticsCode);
            entity.setSenderName(senderName);
            entity.setSenderMobile(senderMobile);
            entity.setSenderProvince(senderProvince);
            entity.setSenderCity(senderCity);
            entity.setSenderDistrict(senderDistrict);
            entity.setSenderDetailAddress(sendAddressStr);

            entity.setCarrierCode(carrier.getCode());
            entity.setCarrierType(carrierType);
            entity.setCarrierName(carrierName);
            entity.setTemplateUrl(templateAddress);
            entity.setSourcePlatformCode(sourcePlatformCode);
        } else {
            //没有匹配承运商时，默认承运商类型为快递，便于在异常单里显示
            entity.setCarrierType("express");
        }
        //if (StrUtil.isNotEmpty(entity.getReceiverProvince())) {
        //    entity.setReceiverProvince(areaCache.translateProvince(entity.getReceiverProvince()));
        //}
        //if (StrUtil.isNotEmpty(entity.getReceiverCity())) {
        //    entity.setReceiverCity(areaCache.translateCity(entity.getReceiverCity()));
        //}
        //if (StrUtil.isNotEmpty(entity.getReceiverDistrict())) {
        //    entity.setReceiverDistrict(areaCache.translateCounty(entity.getReceiverDistrict()));
        //}
        // 新增/保存
        if (isCreated) {
            super.save(entity);
        } else {
            this.updateByCode(entity);
        }

        iDeliveryItemService.remove(new QueryWrapper<DeliveryItem>().lambda().eq(DeliveryItem::getDeliveryCode, entity.getCode()).eq(DeliveryItem::getGroupCode,groupCode));
        iDeliveryItemService.saveBatch(deliveryItems);

        return Result.ok("保存成功");
    }

    /**
     * 更新订单的发货信息
     *
     * @param entity
     * @return
     */
    private void updateDeliverySenderInfo(Delivery entity) {
        Carrier carriers = carrierService.getOne(new LambdaQueryWrapper<Carrier>()
                .eq(Carrier::getGroupCode,UserUtil.getBranchCode())
                .eq(Carrier::getShipmentCode, entity.getShipmentCode())
                .eq(Carrier::getDeleted, 0)
                .eq(Carrier::getCarrierType, CarrierTypeEnum.EXPRESS.getValue())
                .eq(Carrier::getName, entity.getCarrierName())
                .eq(Carrier::getStatus, "1"));

        if (null != carriers) {
            entity.setSenderProvince(carriers.getProvince());
            entity.setSenderCity(carriers.getCity());
            entity.setSenderDistrict(carriers.getCounty());
            entity.setSenderDetailAddress(carriers.getAddress());
            entity.setSenderName(carriers.getSenderName());
            entity.setSenderMobile(carriers.getSenderMobile());
            entity.setCarrierType(carriers.getCarrierType());
        }
    }

    @Override
    public BigDecimal getTotalNum(Delivery delivery) {
        if (null == delivery || null == delivery.getDeliveryItemList() || delivery.getDeliveryItemList().size() <= 0) {
            return null;
        }
        BigDecimal totalNum = BigDecimal.ZERO;
        for (int i = 0; i < delivery.getDeliveryItemList().size(); i++) {
            DeliveryItem deliveryItem = delivery.getDeliveryItemList().get(i);
            BigDecimal quantity = deliveryItem.getQuantity();
            BigDecimal basicQuantity = deliveryItem.getBasicQuantity();
            if (deliveryItem.getQuantity().compareTo(BigDecimal.ZERO) > 0 && deliveryItem.getBasicQuantity().compareTo(BigDecimal.ZERO) > 0) {
                totalNum = totalNum.add(quantity.multiply(basicQuantity));
            }
        }
        return totalNum;
    }

    /**
     * 取消分配
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result cancelDistribution(DeliveryVO entity) {
        // 判断状态是否可取消分配
        Delivery delivery = getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, entity.getCode()).eq(Delivery::getGroupCode,UserUtil.getBranchCode()));
        // 取消前的处理，如校验单据状态、清除生成的上架单信息
        List<DeliveryItemWithDistributionVO> deliveryItemWithDistributionVOS = preCancelDistribution(entity, delivery);
        // 如果处理结果为null，则说明没有可用的取消信息，可以直接返回
        if (IterUtil.isEmpty(deliveryItemWithDistributionVOS)) {
            return Result.ok();
        }

        Map<String, BigDecimal> itemUpdateInfo = new HashMap<>();
        BigDecimal deliveryUpdateQuantity = BigDecimal.ZERO;
        DeliveryDistributionDTO deliveryDistributionDTO;
        Inventory tempInventory;
        int compareQuantityFlag = 0;
        BigDecimal canCancelQuantity;
        // 遍历要取消的分配信息
        for (DeliveryItemWithDistributionVO itemDistribution : deliveryItemWithDistributionVOS) {
            canCancelQuantity = itemDistribution.getDistributionQuantity();
            canCancelQuantity = canCancelQuantity == null ? BigDecimal.ZERO : canCancelQuantity;
            compareQuantityFlag = canCancelQuantity.compareTo(itemDistribution.getCancelQuantity());
            // 若完全取消，则删除分配表，若不完全取消，则更新分配表
            if (compareQuantityFlag == 0) {
                iDeliveryDistributionService.remove(new QueryWrapper<DeliveryDistribution>().lambda().eq(DeliveryDistribution::getCode, itemDistribution.getCode()).eq(DeliveryDistribution::getGroupCode,UserUtil.getBranchCode()));
                // 如果全取消掉，则会调整发货单状态，改为“待分配”
                updateDeliveryStatusIfEmptyDistribution(itemDistribution.getDeliveryCode(),UserUtil.getBranchCode());
            } else if (compareQuantityFlag > 0) {
                // 不完全取消，更新分配表，如果调整后有其他相同的数据，则需要合并
                DeliveryDistribution sameDistribution = iDeliveryDistributionService.hasSameDistribution(itemDistribution.getDeliveryItemCode(), itemDistribution.getCode());
                if (sameDistribution != null) {
                    // 计算出剩余的分配数量
                    BigDecimal remainQuantity = canCancelQuantity.subtract(itemDistribution.getCancelQuantity());
                    // 如果有相同的分配单，则删除当前分配单，改为调整相同的那个分配单数量
                    deliveryDistributionDTO = new DeliveryDistributionDTO();
                    deliveryDistributionDTO.setCode(itemDistribution.getCode());
                    deliveryDistributionDTO.setDistributionQuantity(remainQuantity);
                    deliveryDistributionDTO.setCode(sameDistribution.getCode());
                    iDeliveryDistributionService.remove(new QueryWrapper<DeliveryDistribution>().lambda().eq(DeliveryDistribution::getCode, itemDistribution.getCode()).eq(DeliveryDistribution::getGroupCode,UserUtil.getBranchCode()));
                    iDeliveryDistributionService.adjustDistributionQuantity(deliveryDistributionDTO);
                } else {
                    // 如果没有相同分配单，则调整当前分配单，减掉取消数量、清除拣货单code
                    iDeliveryDistributionService.update(new UpdateWrapper<DeliveryDistribution>().lambda()
                            .eq(DeliveryDistribution::getGroupCode,UserUtil.getBranchCode())
                            .eq(DeliveryDistribution::getCode, itemDistribution.getCode())
                            .set(DeliveryDistribution::getPickingCode, StrUtil.EMPTY)
                            .setSql(" distribution_quantity = distribution_quantity - " + itemDistribution.getCancelQuantity()));
                }
            } else {
                // 取消数量超过了可退数量，需要回滚
                throw new ServiceException("取消数量超过了可退数量，请确认！");
            }
            // 释放部分临时库存
            tempInventory = iInventoryService.getOne(new QueryWrapper<Inventory>().lambda().eq(Inventory::getCode, itemDistribution.getInventoryCode()).eq(Inventory::getGroupCode,UserUtil.getBranchCode()));
            iInventoryService.returnInventory(tempInventory, itemDistribution.getCancelQuantity());

            // 判断是否要将临时库存作业状态转为正常
            iInventoryService.releaseEmptyTempInvJobStatus(tempInventory.getCode());

            // 记录库存变动日志
            Inventory inv = iInventoryService.getOne(new QueryWrapper<Inventory>().lambda().eq(Inventory::getCode, tempInventory.getOldInventoryCode()).eq(Inventory::getGroupCode,UserUtil.getBranchCode()));
            iInventoryRecordService.insertRecord(tempInventory, delivery.getCode(), itemDistribution.getCancelQuantity().negate(), InventoryRecordTypeEnum.PICK.getValue());
            iInventoryRecordService.insertRecord(inv, delivery.getCode(), itemDistribution.getCancelQuantity(), InventoryRecordTypeEnum.PICK.getValue());

            // 根据item的code分组累计取消量
            itemUpdateInfo.put(itemDistribution.getDeliveryItemCode(),
                    itemUpdateInfo.getOrDefault(itemDistribution.getDeliveryItemCode(), BigDecimal.ZERO).add(itemDistribution.getCancelQuantity()));
            //  累计总取消量
            deliveryUpdateQuantity = deliveryUpdateQuantity.add(itemDistribution.getCancelQuantity());
        }

        // 更新发货单、明细的状态、分配数量
        updateCancelInfo(delivery, deliveryUpdateQuantity, itemUpdateInfo);

        return Result.ok();
    }

    /**
     * 取消前校验、处理部分数据
     *
     * @param entity
     * @param delivery
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    public List<DeliveryItemWithDistributionVO> preCancelDistribution(DeliveryVO entity, Delivery delivery) {
        if (ObjectUtil.isNull(delivery)) {
            throw new ServiceException("单据不存在，请确认");
        }

        if (!(StrUtil.equalsAny(delivery.getStatus(), DeliveryStatusEnum.DISTRIBUTIONED.getValue(), DeliveryStatusEnum.PART_DISTRIBUTION.getValue()))) {
            throw new ServiceException("发货单当前状态不能取消");
        }

        // 根据所有上架单状态判断是否可以取消分配，没有上架单的则可以取消分配
        List<DeliveryItemWithDistributionVO> deliveryItemWithDistributionVOS = entity.getDeliveryItemWithDistributionVOList();
        // 过滤出要取消的分配信息
        deliveryItemWithDistributionVOS = deliveryItemWithDistributionVOS.stream().filter(e -> e.getCancelQuantity() != null
                && e.getCancelQuantity().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());

        // 获取所有要取消的分配单号
        List<String> distributionCodes = deliveryItemWithDistributionVOS.stream().map(e -> e.getCode()).collect(Collectors.toList());
        // 如果没有取消的信息，则直接返回null
        if (deliveryItemWithDistributionVOS.isEmpty()) {
            return deliveryItemWithDistributionVOS;
        }
        // 从数据库中获取所有要取消的分配信息
        List<DeliveryDistribution> distributions = iDeliveryDistributionService.list(new QueryWrapper<DeliveryDistribution>().lambda()
                .in(DeliveryDistribution::getCode, distributionCodes));
        // 分组获取拣货单号
        List<String> pickingCodes = distributions.stream().map(e -> e.getPickingCode()).collect(Collectors.toList());
        // 查询这些拣货单中上架数量超过0的数据
        List<Picking> cannotCancelPickings = iPickingService.list(new QueryWrapper<Picking>().lambda().in(Picking::getCode, pickingCodes)
                .gt(Picking::getPickingQuantity, BigDecimal.ZERO).eq(Picking::getGroupCode,UserUtil.getBranchCode()));
        String cannotCancelPickingCodes = cannotCancelPickings.stream().map(e -> e.getCode()).collect(Collectors.joining(","));
        // 如果存在上架数量超过0的，则不允许取消
        if (IterUtil.isNotEmpty(cannotCancelPickings)) {
            throw new ServiceException("上架单【" + cannotCancelPickingCodes + "】已产生上架信息，无法取消");
        }
        // 如果上架单已下发生成任务则也不可以取消
        pickingCodes.forEach(e -> {
            boolean taskFlag = iWmsTaskService.hasUnFinishTaskForSourceCode(e);
            if (taskFlag) {
                throw new ServiceException("拣货单【" + e + "】已生成任务，且任务未完成，无法取消分配");
            }
        });

        // 删除所有要取消的上架单、上架单明细、上架确认单
        iPickingRegisterService.remove(new QueryWrapper<PickingRegister>().lambda().in(PickingRegister::getPickingCode, pickingCodes).eq(PickingRegister::getGroupCode, UserUtil.getBranchCode()));
        iPickingItemService.remove(new QueryWrapper<PickingItem>().lambda().in(PickingItem::getPickingCode, pickingCodes).eq(PickingItem::getGroupCode,UserUtil.getBranchCode()));
        iPickingService.remove(new QueryWrapper<Picking>().lambda().in(Picking::getCode, pickingCodes).eq(Picking::getGroupCode,UserUtil.getBranchCode()));
        // 清除掉要取消的分配单上的上架单信息
        iDeliveryDistributionService.update(new UpdateWrapper<DeliveryDistribution>().lambda().in(DeliveryDistribution::getPickingCode, pickingCodes).eq(DeliveryDistribution::getGroupCode, UserUtil.getBranchCode())
                .set(DeliveryDistribution::getPickingCode, StrUtil.EMPTY));

        return deliveryItemWithDistributionVOS;
    }


    /**
     * 取消分配后，需要更新发货单、发货明细的状态、分配数量
     *
     * @param delivery
     * @param deliveryUpdateQuantity
     * @param itemUpdateInfo
     */
    @Transactional(rollbackFor = {Exception.class})
    public void updateCancelInfo(Delivery delivery, BigDecimal deliveryUpdateQuantity, Map<String, BigDecimal> itemUpdateInfo) {
        // 更新主表总分配量、状态（部分分配或待分配)
        LambdaUpdateWrapper<Delivery> updateWrapper = new UpdateWrapper<Delivery>().lambda();
        updateWrapper.eq(Delivery::getCode, delivery.getCode()).eq(Delivery::getGroupCode,UserUtil.getBranchCode());
        updateWrapper.setSql(" distribution_num = distribution_num + " + deliveryUpdateQuantity.negate());
        delivery = getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, delivery.getCode()).eq(Delivery::getGroupCode,UserUtil.getBranchCode()));
        if (delivery.getDistributionNum().compareTo(BigDecimal.ZERO) > 0 && delivery.getDistributionNum().compareTo(deliveryUpdateQuantity) > 0) {
            updateWrapper.set(Delivery::getStatus, DeliveryStatusEnum.PART_DISTRIBUTION.getValue());
        } else {
            updateWrapper.set(Delivery::getStatus, DeliveryStatusEnum.UN_DISTRIBUTION.getValue());
        }
        this.update(updateWrapper);

        // 更新子表状态、分配数量
        DeliveryItemDTO deliveryItemDTO;
        for (String itemCode : itemUpdateInfo.keySet()) {
            deliveryItemDTO = new DeliveryItemDTO();
            deliveryItemDTO.setCode(itemCode);
            deliveryItemDTO.setDistributionQuantity(itemUpdateInfo.get(itemCode).negate());
            iDeliveryItemService.adjustDistributionQuantity(deliveryItemDTO);
        }
    }

    /**
     * 更新发货数量
     *
     * @param deliveryDTO
     * @return
     */
    @Override
    public int updateSendNum(DeliveryDTO deliveryDTO) {
        return this.baseMapper.updateSendNum(deliveryDTO);
    }

    /**
     * 拣货确认时更新订单拣货数量
     *
     * @param entity
     * @return
     */
    @Override
    public int updatePickingQuantity(Picking entity) {
        entity.setGroupCode(UserUtil.getBranchCode());
        return this.baseMapper.updatePickingQuantity(entity);
    }

    @Override
    public List<DeliveryVO> queryCanRefundList(DeliveryDTO deliveryDTO) {
        deliveryDTO.setGroupCode(UserUtil.getBranchCode());
        return this.baseMapper.queryCanRefundList(deliveryDTO);
    }

    /**
     * 作废单据
     *
     * @param code
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public Result blank(String code) {
        Delivery delivery = getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, code).eq(Delivery::getDeleted, 0).eq(Delivery::getGroupCode,UserUtil.getBranchCode()));
        if (delivery == null) {
            return Result.error("单据不存在，请确认");
        }

        if (!DeliveryStatusEnum.ADD.getValue().equals(delivery.getStatus())) {
            return Result.error("非新增状态单据不允许作废");
        }

        delivery.setStatus(DeliveryStatusEnum.VOID.getValue());
        int updateNum = this.updateByCode(delivery);
        if (updateNum > 0) {
            iDeliveryItemService.updateStatusByDeliveryCode(code, DeliveryStatusEnum.VOID.getValue());
        }

        return Result.ok("作废成功");
    }

    @Override
    public DeliveryVO getDeliveryVoByEntity(Delivery delivery) {
        DeliveryVO deliveryVO = new DeliveryVO();
        BeanUtil.copyProperties(delivery, deliveryVO);
        deliveryVO.setShipmentName(shipmentCache.translate(deliveryVO.getShipmentCode()));
        deliveryVO.setSupplierName(supplierCache.translate(deliveryVO.getSupplierCode()));
        deliveryVO.setCustomerName(customerCache.translate(deliveryVO.getCustomerCode()));
        deliveryVO.setWarehouseName(warehouseCache.translate(deliveryVO.getWarehouseCode()));
        deliveryVO.setCustomerName(customerCache.translate(deliveryVO.getCustomerCode()));
        deliveryVO.setTypeText(DeliveryTypeEnum.translate(deliveryVO.getType()));

        return deliveryVO;
    }

    @Override
    public int adjustDistributionNum(DeliveryDTO deliveryDTO) {
        deliveryDTO.setGroupCode(UserUtil.getBranchCode());
        return this.baseMapper.adjustDistributionNum(deliveryDTO);
    }

    @Override
    public DeliveryVO getVOByCode(String code) {
        Delivery delivery = this.getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, code).eq(Delivery::getGroupCode,UserUtil.getBranchCode()));
        DeliveryVO deliveryVO = getDeliveryVoByEntity(delivery);
        List<DeliveryItemWithDistributionVO> deliveryItemWithDistributionVOS;
        // 获取带分配信息的明细信息
        deliveryItemWithDistributionVOS = iDeliveryDistributionService.queryWithItemList(delivery.getCode());
        deliveryItemWithDistributionVOS.stream().forEach(e -> {
            //订单状态为新增和待分配时，打印计划数量为实际计划数量，其他状态时，打印计划数量为分配数量
            if (StrUtil.equalsAny(delivery.getStatus(),DeliveryStatusEnum.ADD.getValue(),DeliveryStatusEnum.UN_DISTRIBUTION.getValue())) {
                e.setPrintPlanQuantity(e.getTotalNum());
            } else {
                e.setPrintPlanQuantity(e.getDistributionQuantity());
            }
        });
        // 翻译
        deliveryVO.setSupplierName(supplierCache.translate(deliveryVO.getSupplierCode()));
        deliveryVO.setTypeText(DeliveryTypeEnum.translate(delivery.getType()));
        deliveryVO.setGroupName(partyGroupCache.translate(deliveryVO.getGroupCode()));
        deliveryVO.setDeliveryItemWithDistributionVOList(deliveryItemWithDistributionVOS);
        if (StrUtil.isNotBlank(delivery.getCustomerCode())) {
            deliveryVO.setCustomerName(customerCache.translate(deliveryVO.getCustomerCode()));
        } else {
            deliveryVO.setCustomerName(delivery.getReceiverName());
        }


        if (StrUtil.isNotBlank(delivery.getLogisticsCode())) {
            String logisticsName = LogisticsUtils.translate(delivery.getLogisticsCode());
            if (StrUtil.isNotBlank(logisticsName)) {
                deliveryVO.setLogisticsName(logisticsName);
            }
        }

        return deliveryVO;
    }

    /**
     * @param deliveryCode :
     * @description 根据分配确认单中的拣货数量更新主单的拣货数量
     * @author xuyang
     * @date 2020/10/26 11:07
     **/
    @Override
    public int updatePickNumByDistributionData(String deliveryCode) {
        return this.baseMapper.updatePickNumByDistributionData(deliveryCode,UserUtil.getBranchCode());
    }


    /**
     * 更新退货数量
     *
     * @param deliveryCode
     */
    @Override
    public void updateRefundQuantity(String deliveryCode) {
        this.baseMapper.updateRefundQuantity(deliveryCode,UserUtil.getBranchCode());
    }

    /**
     * 更新wms的来源单信息，判断发货单来源类型，回填来源单、来源单item的实际出库数量
     *
     * @param delivery
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void updateWmsSourceQuantity(Delivery delivery, Outbound outbound, List<OutboundItem> outboundItems) {
        String groupCode = delivery.getGroupCode();
        // 判断发货单是否已分配且所有出库单都已完成
        boolean allOutboundFinishFlag = isDeliveryAndOutboundAllFinish(delivery.getCode(),groupCode);
        // 获取发货单明细
        List<DeliveryItem> deliveryItems = iDeliveryItemService.list(new QueryWrapper<DeliveryItem>().lambda().eq(DeliveryItem::getGroupCode,groupCode).eq(DeliveryItem::getDeliveryCode, delivery.getCode()));
        switch (DeliveryTypeEnum.getEnumByValue(delivery.getType())) {
            case OUT_LINGYONG:
                // 更新领用单出库数量
                updateSourceQuantityForLingYong(delivery, outbound, outboundItems);
                break;
            case OUT_BAOSUN:
                // 更新报损单出库数量
                updateSourceQuantityForBaoSun(delivery, outbound, outboundItems);
                break;
            case PURCHASE:
                // wms采购退货，需要确定当前出库单是最终的出库单才能释放可退数量
                if (allOutboundFinishFlag) {
                    updateSourceQuantityForPurchase(delivery, deliveryItems);
                }
                break;
            case ALLOCATION:
                // 更新调拨单出库数量
                updateSourceQuantityForAllocation(delivery, deliveryItems, outbound, outboundItems, allOutboundFinishFlag);
                // 如果整个单据都已完成，则会生成调入仓的收货单或oms入库单
               /* if (allOutboundFinishFlag) {
                    omsAllocationService.afterWmsFinishAllOutbound(delivery.getSourceCode());
                }*/
                break;
            default:
                break;
        }
    }

    /**
     * 更新领用单实际出库数量
     *
     * @param delivery
     * @param outbound
     * @param outboundItems
     */
    @Transactional(rollbackFor = {Exception.class})
    public void updateSourceQuantityForLingYong(Delivery delivery, Outbound outbound, List<OutboundItem> outboundItems) {
        String groupCode = delivery.getGroupCode();
        iCollectService.update(new UpdateWrapper<Collect>().lambda().eq(Collect::getCode, delivery.getSourceCode())
                .eq(Collect::getGroupCode,groupCode)
                .setSql(" total_actual_unit_quantity = total_actual_unit_quantity + " + outbound.getOutUnitQuantity())
                .setSql(" total_actual_quantity = total_actual_quantity + " + outbound.getOutQuantity()));

        outboundItems.stream().forEach(e -> {
            iCollectItemService.update(new UpdateWrapper<CollectItem>().lambda().eq(CollectItem::getCode, e.getSourceItemCode())
                    .eq(CollectItem::getGroupCode,groupCode)
                    .setSql(ACTUAL_QUANTITY_EQUALS_ACTUAL_QUANTIT + e.getOutboundQuantity())
                    .setSql(" actual_unit_quantity = actual_unit_quantity + " + e.getOutboundUnitQuantity()));
        });
    }

    /**
     * 更新报损单实际出库数量
     *
     * @param delivery
     * @param outbound
     * @param outboundItems
     */
    @Transactional(rollbackFor = {Exception.class})
    public void updateSourceQuantityForBaoSun(Delivery delivery, Outbound outbound, List<OutboundItem> outboundItems) {
        String groupCode = delivery.getGroupCode();
        iDamagedService.update(new UpdateWrapper<Damaged>().lambda().eq(Damaged::getCode, delivery.getSourceCode())
                .eq(Damaged::getGroupCode,groupCode)
                .setSql(" total_actual_unit_quantity = total_actual_unit_quantity + " + outbound.getOutUnitQuantity())
                .setSql(" total_actual_quantity = total_actual_quantity + " + outbound.getOutQuantity()));

        outboundItems.stream().forEach(e -> {
            iDamagedItemService.update(new UpdateWrapper<DamagedItem>().lambda().eq(DamagedItem::getCode, e.getSourceItemCode())
                    .eq(DamagedItem::getGroupCode,groupCode)
                    .setSql(ACTUAL_QUANTITY_EQUALS_ACTUAL_QUANTIT + e.getOutboundQuantity())
                    .setSql(" actual_unit_quantity = actual_unit_quantity + " + e.getOutboundUnitQuantity()));
        });
    }


    /**
     * 更新收货退货单实际出库数量
     *
     * @param delivery
     * @param deliveryItems
     */
    @Transactional(rollbackFor = {Exception.class})
    public void updateSourceQuantityForPurchase(Delivery delivery, List<DeliveryItem> deliveryItems) {
        String groupCode = delivery.getGroupCode();
        BigDecimal totalDiffUnitQuantity = BigDecimal.ZERO;
        BigDecimal totalDiffQuantity = BigDecimal.ZERO;
        for (DeliveryItem e : deliveryItems) {
            // 计算基本单位下实际出库与计划出库的差值，相等则不用调整采购明细的已退数量
            BigDecimal diffBasicQuantity = e.getTotalNum().subtract(e.getOutboundQuantity());
            if (diffBasicQuantity.compareTo(BigDecimal.ZERO) == 0) {
                return;
            }
            // 计算出库实际数量（销售单位）、 计算计划数量与实际数量的差值
            BigDecimal diffUnitQuantity = e.getQuantity().subtract(e.getOutboundUnitQuantity());

            // 调整收货单明细退货数量
            iReceiveItemService.update(new UpdateWrapper<ReceiveItem>().lambda().eq(ReceiveItem::getCode, e.getSourceItemCode())
                    .setSql(" refund_quantity = refund_quantity - " + diffUnitQuantity)
                    .setSql(" refund_basic_quantity = refund_basic_quantity - " + diffBasicQuantity)
                    .apply(" 0 <= refund_basic_quantity - " + diffUnitQuantity));

            totalDiffQuantity = totalDiffQuantity.add(diffBasicQuantity);
            totalDiffUnitQuantity = totalDiffUnitQuantity.add(diffUnitQuantity);
        }
        // 调整更新收货单退货数量
        if (totalDiffQuantity.compareTo(BigDecimal.ZERO) > 0) {
            iReceiveService.update(new UpdateWrapper<Receive>().lambda().eq(Receive::getCode, delivery.getSourceCode())
                    .eq(Receive::getGroupCode,groupCode)
                    .setSql(" refund_quantity = refund_quantity - " + totalDiffUnitQuantity)
                    .setSql(" refund_basic_quantity = refund_basic_quantity - " + totalDiffQuantity)
                    .apply(" 0 <= refund_basic_quantity - " + totalDiffQuantity));
        }
    }

    /**
     * 更新收货退货单实际出库数量
     *
     * @param delivery
     * @param outbound
     * @param outboundItems
     */
    @Transactional(rollbackFor = {Exception.class})
    public void updateSourceQuantityForAllocation(Delivery delivery, List<DeliveryItem> deliveryItems,
                                                  Outbound outbound, List<OutboundItem> outboundItems, boolean allOutboundFinishFlag) {
        String groupCode = delivery.getGroupCode();
        iAllocationService.update(new UpdateWrapper<Allocation>().lambda().eq(Allocation::getCode, delivery.getSourceCode())
                .eq(Allocation::getGroupCode,groupCode)
                .setSql(" outbound_unit_quantity = outbound_unit_quantity + " + outbound.getOutUnitQuantity())
                .setSql(" outbound_quantity = outbound_quantity + " + outbound.getOutQuantity()));

        outboundItems.stream().forEach(e -> {
            iAllocationItemService.update(new UpdateWrapper<AllocationItem>().lambda().eq(AllocationItem::getCode, e.getSourceItemCode())
                    .eq(AllocationItem::getGroupCode,groupCode)
                    .setSql(" outbound_quantity = outbound_quantity + " + e.getOutboundQuantity())
                    .setSql(" outbound_unit_quantity = outbound_unit_quantity + " + e.getOutboundUnitQuantity()));
        });
        Allocation allocation = iAllocationService.getOne(new QueryWrapper<Allocation>().lambda().eq(Allocation::getCode, delivery.getSourceCode()).eq(Allocation::getGroupCode,groupCode));
        //erp进行调拨的时候，无需生成入库单
        if (null == allocation) {
            return;
        }
        // 必须要是完成所有出库单后才能 生成下一个仓库的收货主单
        if (allOutboundFinishFlag) {
            Receive receive = new Receive();
            BeanUtils.copyProperties(delivery, receive);
            receive.setCode(OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.FH, "",groupCode));
            receive.setSourceCode(delivery.getSourceCode());
            receive.setType(ReceiveTypeEnum.ALLOCATION.getValue());
            receive.setStatus(ReceiveStatusEnum.WAITING_RECEIVE.getValue());
            receive.setCreatedBy(UserUtil.getCode());
            receive.setGmtCreated(LocalDateTime.now());
            receive.setTotalQuantity(delivery.getSendNum());
            //收货的仓库应为调拨单的调入库
            receive.setWarehouseCode(allocation.getTargetWarehouseCode());
            // 生成收货单明细
            List<ReceiveItem> receiveItemList = Lists.newArrayList();
            deliveryItems.stream().forEach(item -> {
                // 发货数量为零时，不形成待收货单
                if (item.getOutboundQuantity().compareTo(BigDecimal.ZERO) > 0) {
                    ReceiveItem receiveItem = new ReceiveItem();
                    BeanUtils.copyProperties(item, receiveItem);
                    receiveItem.setCode(IdUtil.simpleUUID());
                    receiveItem.setReceiveCode(receive.getCode());
                    receiveItem.setPlanQuantity(item.getOutboundQuantity());
                    receiveItem.setCreatedBy(UserUtil.getCode());
                    receiveItem.setGmtCreated(LocalDateTime.now());
                    receiveItem.setUnitConvertQuantity(item.getBasicQuantity());
                    receiveItem.setPlanQuantity(item.getOutboundQuantity());
                    receiveItem.setUnitConvertQuantity(item.getBasicQuantity());
                    receiveItemList.add(receiveItem);
                }
            });

            BigDecimal totalQuantity = receiveItemList.stream().map(ReceiveItem::getPlanQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
            receive.setTotalQuantity(totalQuantity);
            receive.setReceiveItemList(receiveItemList);
            // 保存收货单、收货单明细
            iReceiveService.save(receive);
        }
    }

    /**
     * @param outbound      :
     * @param outboundItems :
     * @description 发货单在出库操作后的操作，如回填oms单据
     * @author xuyang
     * @date 2020/12/15 13:54
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void afterOutbound(Outbound outbound, List<OutboundItem> outboundItems) {
        String groupCode = outbound.getGroupCode();
        Delivery delivery = this.getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, outbound.getDeliveryCode()).eq(Delivery::getGroupCode,groupCode));
        if (ObjectUtil.isNull(delivery)) {
            throw new ServiceException("关联的发货单【" + outbound.getDeliveryCode() + "】不存在");
        }

        // 非oms下发，则做进一步判断，更新单据信息
        if (NUM_ZERO.equals(delivery.getIsOmsOrder())) {
            updateWmsSourceQuantity(delivery, outbound, outboundItems);
        } else {
            Map<String, BigDecimal[]> sourceItemOutQuantity = getSourceItemQuantity(outboundItems);

            switch (DeliveryTypeEnum.getEnumByValue(delivery.getType())) {
                case OUT_SALE:
                    // oms下发，如果是销售单，则更新销售单信息，生成oms出库单
                    updateOmsSaleInfo(delivery, outbound, sourceItemOutQuantity);
                    //订单发货确认
                    Result result = deliveryConfirm(delivery.getSourceCode(),delivery.getWavePickingCode());
                    if ((int)result.get(CommonConst.STATUS) == 0) {
                        // 普通销售单及符合发货确认的订单，生成oms出库单
                        iSaleService.wmsSaveOmsOutboundBySale(delivery.getSourceCode(), outbound, sourceItemOutQuantity);
                    } else{
                        throw new ServiceException((String) result.get(CommonConst.MSG));
                    }
                    break;
                case ALLOCATION:
                    // 调拨单
                    omsAllocationService.updateAllocation(delivery.getCode(), outbound, sourceItemOutQuantity);
                    // 调拨单生成oms出库单
//                    omsAllocationService.wmsSaveOmsOutboundByAllocation(delivery.getSourceCode(), outbound, sourceItemOutQuantity);
                    // OMS主单由调拨单主单构建，OMS明细由WMS出库单明细构建
                    omsAllocationService.wmsSaveOmsOutboundByAllocationOutBound(delivery.getSourceCode(), outbound);
                    // 判断发货单、出库单所有流程是否完结，完结则触发完结逻辑
                    if (isDeliveryAndOutboundAllFinish(delivery.getCode(),groupCode)) {
                        omsAllocationService.afterWmsFinishAllOutbound(delivery.getSourceCode());
                    }
                    break;
                case PURCHASE:
                    // oms采购退货单生成的出库单，则更新oms采购退货单实际出库数量
                    updateOmsPurchaseReturnInfo(delivery);
                    // 根据Oms采购退货单出库单，生成负的入库单
                    createOmsInboundInfo(delivery, outbound, outboundItems);
                    // 判断发货单、出库单所有流程是否完结，完结则调整Oms采购单的退货数量
                    if (isDeliveryAndOutboundAllFinish(delivery.getCode(),groupCode)) {
                        adjustPurchaseRefundQuantity(delivery.getSourceCode());
                    }
                    break;
                default:
                    break;
            }
        }


    }


    /**
     * 采退单流程完结后，根据采退单实际出库数量调整退货数量
     * @param purchaseReturnCode
     */
    @Transactional(rollbackFor = {Exception.class})
    public void adjustPurchaseRefundQuantity(String purchaseReturnCode){
        //找到对应的采购退货单
        OmsPurchaseReturnVO omsPurchaseReturn = omsPurchaseReturnService.getByCode(purchaseReturnCode);
        //根据采购退货单修改采购单的退货数量
        List<OmsPurchaseReturnItemVO> omsPurchaseReturnItemList = omsPurchaseReturn.getOmsPurchaseReturnItemList();
        BigDecimal diffSumQuantity =BigDecimal.ZERO;
        BigDecimal diffSumUnitQuantity =BigDecimal.ZERO;
        for (OmsPurchaseReturnItemVO e : omsPurchaseReturnItemList){
            //根据采购退货单修改采购单的退货数量
            if (ObjectUtil.isNotNull(e.getActualBasicQuantity())) {
                //基本单位
                BigDecimal diffQuantity = e.getActualBasicQuantity().subtract(e.getPlanBasicQuantity());
                //退货单位
                BigDecimal diffUnitQuantity = e.getActualQuantity().subtract(e.getPlanQuantity());
                if (diffQuantity.compareTo(BigDecimal.ZERO) != 0) {
                    purchaseItemService.update(new UpdateWrapper<PurchaseItem>().lambda().eq(PurchaseItem::getCode,e.getSourceItemCode())
                            .setSql(" refund_basic_quantity = refund_basic_quantity + " + diffQuantity)
                            .setSql(" refund_quantity = refund_quantity + " + diffUnitQuantity));
                    diffSumQuantity=diffSumQuantity.add(diffQuantity);
                    diffSumUnitQuantity=diffSumUnitQuantity.add(diffUnitQuantity);
                }
            }
        }

        // 释放oms的占用库存
        omsPurchaseReturn.getOmsPurchaseReturnItemList().forEach(e->{
            iOmsInventoryService.update(new UpdateWrapper<OmsInventory>().lambda().eq(OmsInventory::getGoodsCode, e.getGoodsCode())
                    .eq(OmsInventory::getWarehouseCode, omsPurchaseReturn.getWarehouseCode())
                    .eq(OmsInventory::getShipmentCode, omsPurchaseReturn.getShipmentCode())
                    .setSql(" use_quantity = use_quantity - " + e.getPlanBasicQuantity()));
        });
        // 更改采购单的明细
        purchaseService.update(new UpdateWrapper<Purchase>().lambda().eq(Purchase::getCode,omsPurchaseReturn.getSourceCode())
                .setSql(" refund_basic_quantity = refund_basic_quantity + " + diffSumQuantity)
                .setSql(" refund_quantity = refund_quantity + " + diffSumUnitQuantity));

    }


    /**
     * 根据发货单回填采购退货单和采购单
     * @param delivery
     */
    @Transactional(rollbackFor = {Exception.class})
    public void updateOmsPurchaseReturnInfo(Delivery delivery){
        //判断有无对应的采购退货单
        OmsPurchaseReturnVO omsPurchaseReturn= omsPurchaseReturnService.getByCode(delivery.getSourceCode());
        if(ObjectUtil.isEmpty(omsPurchaseReturn)){
            throw new ServiceException("关联出货单【" + delivery.getCode() + "】的采购退货单不存在,请检查!");
        }
        List<DeliveryItem> deliveryItemList = iDeliveryItemService.list(new QueryWrapper<DeliveryItem>().lambda().eq(DeliveryItem::getDeliveryCode, delivery.getCode()).eq(DeliveryItem::getGroupCode,UserUtil.getBranchCode()));

        BigDecimal totalBasicQuantity = BigDecimal.ZERO;
        BigDecimal totalQuantity = BigDecimal.ZERO;
        for (DeliveryItem r: deliveryItemList) {
            //找到对应的商品
            Goods goods = goodsCache.getObj(r.getGoodsCode());
            //出库数量
            BigDecimal unitQuantity = r.getOutboundQuantity().divide(r.getBasicQuantity()).setScale(goods.getUnitPrecision().intValue(), BigDecimal.ROUND_HALF_UP);
            omsPurchaseReturnItemService.update(new UpdateWrapper<OmsPurchaseReturnItem>().lambda().eq(OmsPurchaseReturnItem::getPurchaseReturnCode,delivery.getSourceCode())
                    .eq(OmsPurchaseReturnItem::getCode,r.getSourceItemCode())
                    .set(OmsPurchaseReturnItem::getActualBasicQuantity,r.getOutboundQuantity())
                    .set(OmsPurchaseReturnItem::getActualQuantity,unitQuantity)
            );
            totalBasicQuantity = totalBasicQuantity.add(r.getOutboundQuantity());
            totalQuantity = totalQuantity.add(unitQuantity);
        }
        omsPurchaseReturnService.update(new UpdateWrapper<OmsPurchaseReturn>().lambda().eq(OmsPurchaseReturn::getCode, delivery.getSourceCode())
                .set(OmsPurchaseReturn::getActualBasicQuantity, totalBasicQuantity).set(OmsPurchaseReturn::getActualQuantity, totalQuantity));
    }

    /**
     * 生成入库单(负)
     * @param delivery
     */
    public void createOmsInboundInfo(Delivery delivery, Outbound wmsOut, List<OutboundItem> wmsOutItems){
        //判断有无对应的采购退货单
        OmsPurchaseReturnVO omsPurchaseReturn= omsPurchaseReturnService.getByCode(delivery.getSourceCode());
        if(ObjectUtil.isEmpty(omsPurchaseReturn)){
            throw new ServiceException("关联出货单【" + delivery.getCode() + "】的采购退货单不存在,请检查!");
        }
        //根据对应的采购退货单生成入库单
        OmsInbound omsInbound = new OmsInbound();
        BeanUtil.copyProperties(omsPurchaseReturn, omsInbound, false);
        omsInbound.setCode(OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.RK, "",UserUtil.getBranchCode()));
        omsInbound.setSourceCode(omsPurchaseReturn.getCode());
        omsInbound.setType(PurchaseTypeEnum.PURCHASERETURNS.getValue());
        omsInbound.setCreatedBy(UserUtil.getCode());
        omsInbound.setCreatedName(partyCache.translate(UserUtil.getCode()));
        omsInbound.setModifiedBy(UserUtil.getCode());
        omsInbound.setModifiedName(partyCache.translate(UserUtil.getCode()));
        omsInbound.setStatus(InboundStatusEnum.FINISH_IN.getValue());
        omsInbound.setPaymentAmount(BigDecimal.ZERO);
        BigDecimal owedAmount = BigDecimal.ZERO;

        // 根据wms出库明细获取实际出库采购单明细code及数量
        Map<String, BigDecimal[]> purchaseItemQuantity = getSourceItemQuantity(wmsOutItems);

        // 根据wms出库单明细，过滤oms采退单明细，只留下本次出库实际明细
        List<OmsPurchaseReturnItemVO> returnItems = omsPurchaseReturn.getOmsPurchaseReturnItemList().stream()
                .filter(e->purchaseItemQuantity.containsKey(e.getCode())).collect(Collectors.toList());

        //保存入库单明细
        List<OmsInboundItem> inboundItems = returnItems.stream().map(r -> {
            OmsInboundItem item = new OmsInboundItem();
            // 获取实际入库数量
            BigDecimal inboundQuantity = purchaseItemQuantity.get(r.getCode())[NUM_ZERO];
            BeanUtil.copyProperties(r, item, false);
            item.setCode(IdUtil.simpleUUID());
            item.setInboundCode(omsInbound.getCode());
            item.setStorageQuantity(inboundQuantity.negate()); //负数
            item.setTaxMoney(r.getUnitPrice().multiply(inboundQuantity).multiply(r.getTaxRate())
                    .divide(DECIMAL100).setScale(6, BigDecimal.ROUND_HALF_UP).negate());
            item.setAmountMoney(r.getUnitPrice().multiply(inboundQuantity).setScale(6, BigDecimal.ROUND_HALF_UP).negate());
            item.setTotalMoney(r.getUnitPrice().multiply(inboundQuantity).add(
                    r.getUnitPrice().multiply(inboundQuantity).multiply(r.getTaxRate()).divide(DECIMAL100)
            ).setScale(6, BigDecimal.ROUND_HALF_UP).negate());
            item.setCreatedBy(UserUtil.getCode());
            item.setCreatedName(partyCache.translate(UserUtil.getCode()));
            item.setModifiedBy(UserUtil.getCode());
            item.setModifiedName(partyCache.translate(UserUtil.getCode()));
            return item;
        }).collect(Collectors.toList());
        //保存入库单明细
        iOmsInboundItemService.saveBatch(inboundItems);
        for(OmsInboundItem e:inboundItems){
            owedAmount = owedAmount.add(e.getTotalMoney());
        }
        omsInbound.setOwedAmount(owedAmount);
        //保存入库单
        iOmsInboundService.save(omsInbound);
        omsSettlementService.generatePayment(omsInbound);

        //更新库存
        inboundItems.forEach(r -> {
            OmsInventory inventory = iOmsInventoryService.getOne(new QueryWrapper<OmsInventory>().lambda().eq(OmsInventory::getGoodsCode, r.getGoodsCode())
                    .eq(OmsInventory::getWarehouseCode,omsInbound.getWarehouseCode()).eq(OmsInventory::getShipmentCode,omsInbound.getShipmentCode()));
            if (inventory != null) {
                //如果有库存记录,更新库存
                iOmsInventoryService.update(new UpdateWrapper<OmsInventory>().lambda().eq( OmsInventory::getGoodsCode, inventory.getGoodsCode())
                        .eq(OmsInventory::getWarehouseCode,omsInbound.getWarehouseCode())
                        .eq(OmsInventory::getShipmentCode,omsInbound.getShipmentCode())
                        .setSql("quantity=quantity+" + r.getStorageQuantity())
                        .setSql("average_unit_price = (total_amount+"+r.getTotalMoney()+")/quantity")
                        .setSql("total_amount = average_unit_price * quantity"));
            } else {
                throw new ServiceException("商品“" + r.getGoodsName() + "”可用库存不足。");
            }
        });
    }


    /**
     * @description
     * 根据发货单，更新oms销售单信息
     * @author xuyang
     * @date 2020/12/15 14:09
     * @param delivery:
     **/
    private void updateOmsSaleInfo(Delivery delivery, Outbound outbound, Map<String, BigDecimal[]> sourceItemOutQuantity) {
        //判断是否合单
        if (delivery.getSourceCode().startsWith(OrderIdGeneratorUtil.OrderTypeEnum.MG.getCode())) {
            List<Sale> saleList = iSaleService.list(new QueryWrapper<Sale>().lambda().eq(Sale::getMergeCode, delivery.getSourceCode()));
            if (CollectionUtil.isNotEmpty(saleList)) {
                for (Sale sale : saleList) {
                    sale.setWaybillCode(delivery.getWaybillCode());
                    sale.setActualBasicQuantity(sale.getPlanBasicQuantity());
                    sale.setActualQuantity(sale.getPlanQuantity());
                    iSaleItemService.update(new UpdateWrapper<SaleItem>().lambda().eq(SaleItem::getSaleCode, sale.getCode())
                            .setSql(" actual_basic_quantity = plan_basic_quantity ")
                            .setSql(" actual_quantity = plan_quantity "));
                }
                iSaleService.updateBatchById(saleList);
            }
        } else if (delivery.getSourceCode().contains("-")) {
            //表示拆单子单
            boolean allSuccess = true;
            List<String> waybills = new ArrayList<>();
            SalePacks salePacks = iSalePacksService.getOne(new LambdaQueryWrapper<SalePacks>().eq(SalePacks::getCode, delivery.getSourceCode()));
            if (salePacks != null) {
                salePacks.setWaybillCode(delivery.getWaybillCode());
                // 根据出库明细累计更新销售明细的实际出库数量
                sourceItemOutQuantity.forEach((sourceItemCode, outQuantities) -> {
                    iSaleItemService.update(new UpdateWrapper<SaleItem>().lambda().eq(SaleItem::getCode, sourceItemCode)
                            .setSql(" actual_basic_quantity = actual_basic_quantity + " + outQuantities[NUM_ZERO])
                            .setSql(" actual_quantity = actual_quantity + " + outQuantities[NUM_ONE]));
                });
                salePacks.setOrderStatus(SalePacksOrderStatusEnum.HASSEND.getValue());
                iSalePacksService.updateById(salePacks);
                //判断是否所有拆单都已发货
                List<SalePacks> salePacksList = iSalePacksService.list(new LambdaQueryWrapper<SalePacks>().eq(SalePacks::getMasterSaleCode, salePacks.getMasterSaleCode()));
                for (SalePacks sp : salePacksList) {
                    if (sp.getCode().equals(salePacks.getCode()) || sp.getOrderStatus().equals(SalePacksOrderStatusEnum.HASSEND.getValue())) {
                        waybills.add(sp.getWaybillCode());
                    } else {
                        allSuccess = false;
                        break;
                    }
                }
            }

            if (allSuccess) {
                Sale sale = iSaleService.getOne(new QueryWrapper<Sale>().lambda().eq(Sale::getCode, salePacks.getMasterSaleCode()));
                sale.setWaybillCode(StringUtils.join(waybills, ","));
                List<SaleItem> saleItems = iSaleItemService.list(new LambdaQueryWrapper<SaleItem>().eq(SaleItem::getSaleCode, sale.getCode()));
                sourceItemOutQuantity = new HashMap<>();
                for (SaleItem v : saleItems) {
                    BigDecimal[] quantities = new BigDecimal[2];
                    quantities[NUM_ZERO] = v.getPlanBasicQuantity();
                    quantities[NUM_ONE] = v.getPlanQuantity();
                    sourceItemOutQuantity.put(v.getCode(), quantities);
                    v.setActualBasicQuantity(v.getPlanBasicQuantity());
                    v.setActualQuantity(v.getPlanQuantity());
                }
                iSaleItemService.updateBatchById(saleItems);
                sale.setSaleItemList(saleItems);
                // 重新计算销售单的各种金额数据
                sale.setActualQuantity(sale.getPlanQuantity());
                sale.setActualBasicQuantity(sale.getActualBasicQuantity());

                // 更新销售单、销售明细的各种金额数据
                iSaleService.updateById(sale);
                saleItems.forEach(e -> {
                    iSaleItemService.updateById(e);
                });
                delivery.setSourceCode(sale.getCode());
            } else {
                delivery.setSourceCode(null);
            }
        } else if (SaleTypeEnum.NORMAL.getValue().equals(delivery.getOrderType())) {
            Sale sale = iSaleService.getOne(new QueryWrapper<Sale>().lambda().eq(Sale::getCode, delivery.getSourceCode()));
            sale.setWaybillCode(delivery.getWaybillCode());
            // 根据出库明细累计更新销售明细的实际出库数量
            sourceItemOutQuantity.forEach((sourceItemCode, outQuantities)->{
                iSaleItemService.update(new UpdateWrapper<SaleItem>().lambda().eq(SaleItem::getCode, sourceItemCode)
                        .setSql(" actual_basic_quantity = actual_basic_quantity + " + outQuantities[NUM_ZERO])
                        .setSql(" actual_quantity = actual_quantity + " + outQuantities[NUM_ONE]));
            });

            List<SaleItem> saleItems = iSaleItemService.list(new QueryWrapper<SaleItem>().lambda().eq(SaleItem::getSaleCode, sale.getCode()));
            sale.setSaleItemList(saleItems);
            // 重新计算销售单的各种金额数据
            iSaleService.calcQuantity(sale, false);

            // 更新销售单、销售明细的各种金额数据
            iSaleService.updateById(sale);
            saleItems.forEach(e->{
                iSaleItemService.updateById(e);
            });
        } else {
            Sale sale = iSaleService.getOne(new QueryWrapper<Sale>().lambda().eq(Sale::getCode, delivery.getSourceCode()));
            sale.setWaybillCode(delivery.getWaybillCode());
            sale.setActualBasicQuantity(sale.getPlanBasicQuantity());
            sale.setActualQuantity(sale.getPlanQuantity());
            iSaleItemService.update(new UpdateWrapper<SaleItem>().lambda().eq(SaleItem::getSaleCode, sale.getCode())
                    .setSql(" actual_basic_quantity = plan_basic_quantity ")
                    .setSql(" actual_quantity = plan_quantity "));
            iSaleService.updateById(sale);
        }
//        Sale sale = iSaleService.getOne(new QueryWrapper<Sale>().lambda().eq(Sale::getCode, delivery.getSourceCode()));
//        // 根据出库明细累计更新销售明细的实际出库数量
//        sourceItemOutQuantity.forEach((sourceItemCode, outQuantities)->{
//            iSaleItemService.update(new UpdateWrapper<SaleItem>().lambda().eq(SaleItem::getCode, sourceItemCode)
//                    .setSql(" actual_basic_quantity = actual_basic_quantity + " + outQuantities[NUM_ZERO])
//                    .setSql(" actual_quantity = actual_quantity + " + outQuantities[NUM_ONE]));
//        });
//
//        List<SaleItem> saleItems = iSaleItemService.list(new QueryWrapper<SaleItem>().lambda().eq(SaleItem::getSaleCode, sale.getCode()));
//        sale.setSaleItemList(saleItems);
//        // 重新计算销售单的各种金额数据
//        iSaleService.calcQuantity(sale, false);
//
//        // 更新销售单、销售明细的各种金额数据
//        iSaleService.updateById(sale);
//        saleItems.forEach(e->{
//            iSaleItemService.updateById(e);
//        });
    }

    /**
     * @param code :
     * @description 发货退货单主单及明细, 发货退货单修改时不会查看分配信息，但会查看可退数量
     * @author xuyang
     * @date 2020/12/15 19:29
     **/
    @Override
    public DeliveryVO getRefundVOByCode(String code) {
        Delivery delivery = getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, code).eq(Delivery::getGroupCode,UserUtil.getBranchCode()));
        if (ObjectUtil.isNull(delivery)) {
            throw new ServiceException("退货单【" + code + "】不存在");
        }
        DeliveryVO vo = new DeliveryVO();
        BeanUtil.copyProperties(delivery, vo);
        // 翻译
        vo.setSupplierName(supplierCache.translate(vo.getSupplierCode()));
        vo.setGroupName(partyGroupCache.translate(vo.getGroupCode()));
        vo.setWarehouseName(warehouseCache.translate(vo.getWarehouseCode()));
        DeliveryItemDTO itemDTO = new DeliveryItemDTO();
        itemDTO.setDeliveryCode(delivery.getCode());
        List<DeliveryItemVO> deliveryItemVOS = iDeliveryItemService.queryRefundList(itemDTO);
        vo.setDeliveryItemVOList(deliveryItemVOS);

        return vo;
    }

    /**
     * @param entity :
     * @description 保存发货退货单
     * @author xuyang
     * @date 2020/12/18 9:16
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result refundSaveOrSubmit(Delivery entity, boolean submitFlag) {
        String userCode = UserUtil.getCode();
        String userName = partyCache.translate(userCode);
        LocalDateTime currentDateTime = LocalDateTime.now();
        if (StrUtil.isEmpty(entity.getShipmentName())) {
            entity.setShipmentName(shipmentCache.translate(entity.getShipmentCode()));
        }
        if (IterUtil.isEmpty(entity.getDeliveryItemList())) {
            throw new ServiceException("退货明细信息不能为空");
        }

        // 校验单据状态, 添加部分操作信息
        if (StrUtil.isNotBlank(entity.getCode())) {
            Delivery dbEntity = getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, entity.getCode()).eq(Delivery::getGroupCode,UserUtil.getBranchCode()));
            if (ObjectUtil.isNull(dbEntity)) {
                throw new ServiceException("单据【" + entity.getCode() + "】不存在");
            }
            if (!DeliveryStatusEnum.ADD.getValue().equals(dbEntity.getStatus())) {
                throw new ServiceException("单据状态已发生改变，无法保存");
            }
            // 添加修改操作信息
            entity.setId(dbEntity.getId());
            entity.setModifiedBy(userCode);
            entity.setModifiedName(userName);
            entity.setGmtModified(currentDateTime);
            entity.setGroupCode(UserUtil.getBranchCode());
            // 删除数据库中原子单信息
            iDeliveryItemService.remove(new QueryWrapper<DeliveryItem>().lambda().eq(DeliveryItem::getGroupCode,UserUtil.getBranchCode()).eq(DeliveryItem::getDeliveryCode, dbEntity.getCode()));
        } else {
            entity.setCode(OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.FH, entity.getShipmentCode(),UserUtil.getBranchCode()));
            entity.setCreatedBy(userCode);
            entity.setCreatedName(userName);
            entity.setGmtCreated(currentDateTime);
            entity.setGroupCode(UserUtil.getBranchCode());
        }
        // 添加类型等
        entity.setType(DeliveryTypeEnum.PURCHASE.getValue());

        // 校验退货数量是否超过可退数量, 同时更新主单总数
        validRefundItem(entity, submitFlag);
        // 保存主单
        this.saveOrUpdate(entity);
        // 保存子单
        List<DeliveryItem> items = entity.getDeliveryItemList().stream().peek(e -> e.setGroupCode(UserUtil.getBranchCode()))
                .collect(toList());
        entity.setDeliveryItemList(items);
        iDeliveryItemService.saveBatch(entity.getDeliveryItemList());

        return Result.ok();
    }


    /**
     * @param entity:
     * @param submitFlag: 是否是提交操作，true：提交操作；提交时需要占用收货明细已退数量；非提交则只校验数量是否超过
     * @description 1. 校验发货退货单退货数量是否正确，包括格式、可退数量是否超过、退货项是否重复
     * 2. 更新主单总数
     * @author xuyang
     * @date 2020/12/18 9:32
     **/
    @Transactional(rollbackFor = {Exception.class})
    public void validRefundItem(Delivery entity, boolean submitFlag) {
        String userCode = UserUtil.getCode();
        String userName = partyCache.translate(userCode);
        LocalDateTime currentDateTime = LocalDateTime.now();

        // 明细中商品种类
        Map<String, Boolean> goodsSortMap = new HashMap<>();

        Map<String, Object> receiveItemCodes = new HashMap<>();
        BigDecimal totalNum = BigDecimal.ZERO;
        for (DeliveryItem item : entity.getDeliveryItemList()) {
            // 校验关联的收货单明细是否重复
            if (receiveItemCodes.containsKey(item.getSourceItemCode())) {
                throw new ServiceException("商品【" + item.getSourceItemCode() + "】退货信息重复");
            }
            receiveItemCodes.put(item.getSourceItemCode(), true);
            // 校验退货数量格式
            if (ObjectUtil.isNull(item.getQuantity()) || item.getQuantity().compareTo(BigDecimal.ZERO) <= 0) {
                throw new ServiceException("商品【" + item.getGoodsName() + "】退货数量必须大于0");
            }

            goodsSortMap.put(item.getGoodsCode(), true);

            // 调整明细字段
            item.setCode(IdUtil.simpleUUID());
            item.setDeliveryCode(entity.getCode());
            item.setCreatedBy(userCode);
            item.setCreatedName(userName);
            item.setGmtCreated(currentDateTime);
            // 基本单位数量 = 销售单位数量 * 转换率
            item.setTotalNum(item.getQuantity().multiply(item.getBasicQuantity()));


            // 根据是否提交状态，选择是占用可退数量还是只是查询校验可退数量
            if (submitFlag) {
                BigDecimal refundBasicQuantity = item.getQuantity().multiply(item.getBasicQuantity());
                // 占用收货明细退货数量
                boolean refundQuantityFlag = iReceiveItemService.update(new UpdateWrapper<ReceiveItem>().lambda()
                        .eq(ReceiveItem::getCode, item.getSourceItemCode())
                        .setSql(REFUND_QUANTITY_EQUALS_REFUND_QUANTIT + item.getQuantity())
                        .setSql(REFUND_BASIC_QUANTITY_EQUALS_REFUND_BASIC_QUANTIT + refundBasicQuantity)
                        .apply(" inbound_quantity >= refund_quantity + " + item.getQuantity()));
                if (!refundQuantityFlag) {
                    throw new ServiceException("商品【" + item.getGoodsName() + "】退货数量超过可退数量，无法退货");
                }
            } else {
                // 校验是否超过可退数量
                ReceiveItem receiveItem = iReceiveItemService.getOne(new QueryWrapper<ReceiveItem>().lambda().eq(ReceiveItem::getCode, item.getSourceItemCode()));
                BigDecimal diffQuantity = receiveItem.getInboundQuantity().subtract(receiveItem.getRefundQuantity()).subtract(item.getQuantity());
                if (diffQuantity.compareTo(BigDecimal.ZERO) < 0) {
                    throw new ServiceException("商品【" + item.getGoodsName() + "】退货数量超过可退数量，无法退货");
                }
            }

            // 合计子单总数
            totalNum = totalNum.add(item.getQuantity());
        }
        // 更新主单总数
        entity.setTotalNum(totalNum);
        entity.setGoodsSortQuantity(new BigDecimal(Integer.toString(goodsSortMap.size())));
    }




    /**
     * 根据wms出库单明细，汇总获取来源明细单实际出库数量
     *
     * @param wmsOutItems
     * @return
     */
    private Map<String, BigDecimal[]> getSourceItemQuantity(List<OutboundItem> wmsOutItems) {
        Map<String, BigDecimal[]> itemQuantity = new HashMap<>();
        wmsOutItems.stream().forEach(e -> {
            BigDecimal[] actualQuantity;
            if (itemQuantity.containsKey(e.getSourceItemCode())) {
                actualQuantity = itemQuantity.get(e.getSourceItemCode());
                actualQuantity[NUM_ZERO] = actualQuantity[NUM_ZERO].add(e.getOutboundQuantity());
                actualQuantity[NUM_ONE] = actualQuantity[NUM_ONE].add(e.getOutboundUnitQuantity());
            } else {
                actualQuantity = new BigDecimal[NUM_TWO];
                actualQuantity[NUM_ZERO] = e.getOutboundQuantity();
                actualQuantity[NUM_ONE] = e.getOutboundUnitQuantity();
            }
            itemQuantity.put(e.getSourceItemCode(), actualQuantity);
        });
        return itemQuantity;
    }





    /**
     * 校验导入数据必填项是否有空值
     */
    private void validateImportNotBlank(List<DeliveryExcelDTO> deliveryExcelDTOList) {
        for (int index = NUM_ZERO; index < deliveryExcelDTOList.size(); index++) {

            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getWarehouseSerialNumber())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + WAREHOUSE_SERIAL_NUMBER + NOT_BLANK);
            }

            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getWarehouseName())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + WAREHOUSE_NAME + NOT_BLANK);
            }

            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getShipmentSerialNumber())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SHIPMENT_SERIAL_NUMBER + NOT_BLANK);
            }

            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getShipmentName())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SHIPMENT_NAME + NOT_BLANK);
            }

            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getGoodCode())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODSCODE + NOT_BLANK);
            }

            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getGoodsName())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODS_NAME_TEXT + NOT_BLANK);
            }

            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getGoodsUnit())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODS_UNIT + NOT_BLANK);
            }

//            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getPlanQuantity())) {
//                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + QUANTITY + NOT_BLANK);
//            }
        }
    }



    /**
     * 导入预览
     *
     * @param deliveryExcelDTOList
     * @return
     */
    @Override
    public List<DeliveryExcelDTO> deliveryView(List<DeliveryExcelDTO> deliveryExcelDTOList) {

        // 由于发货单只有一个仓库、入驻商，所以可使用第一条数据进行校验仓库、入驻商
        DeliveryExcelDTO firstRow = deliveryExcelDTOList.get(NUM_ZERO);

        // 校验Excel里必填项是否有空值
        validateNotBlank(deliveryExcelDTOList);

        // 校验仓库是否启用并且开启库位管理
        Warehouse warehouse = validateWarehouseLocationManagement(firstRow);

        // 校验Excel里是否有重复数据
        validateRepeatData(deliveryExcelDTOList);

        // 校验 入驻商
        Shipment shipment = validateShipment(firstRow);

        // 校验 仓库 入驻商 是否 绑定
//        boolean bind = iWarehouseShipmentService.isBind(warehouse.getCode(), shipment.getPartyCode());
//        if (!bind) {
//            throw new ServiceException(WAREHOUSE + ENTRANTS_NAME + NOT_BOUND);
//        }
//		Map<String, Object> shipmentMap = shipmentCache.get(shipment.getPartyCode());
        // 校验当前用户、当前仓库是否有权操作导入的仓库、货主信息
        validateAuth(warehouse, shipment);

        //获取所有的用户
        Map<String, Customer> customers = validateCustomerName(deliveryExcelDTOList);

        //获取该仓库下供应商下的承运商
        Map<String, Carrier> stringCarrierMap = validateCarrier(deliveryExcelDTOList, shipment.getPartyCode());

        //是否发货状态
        isDeliveringIfStockout(deliveryExcelDTOList);

        //订单类型判断
        validatetype(deliveryExcelDTOList);

        //单位装换
        Map<String, GoodsUnitConversion> baseGoodsUnitConversion = baseGoodsUnitConversion();


        // 获取货主下所有商品，减少查询次数
        List<Goods> goodsList = iGoodsService.list(new QueryWrapper<Goods>().lambda().eq(Goods::getShipmentCode, shipment.getPartyCode()).eq(Goods::getPartyGroupCode,UserUtil.getBranchCode()));
        Map<String, Goods> goodsMap = goodsList.stream().collect(Collectors.toMap(Goods::getGoodCode, e -> e, (key1, key2) -> key2));

        // 获取货主下所有单位，两种，一种是以code为key的，另一种则是以单位名称为key的
        List<GoodsUnit> units = iGoodsUnitService.list(new QueryWrapper<GoodsUnit>().lambda().eq(GoodsUnit::getShipmentCode, shipment.getPartyCode()).eq(GoodsUnit::getGroupCode,UserUtil.getBranchCode()));
        Map<String, GoodsUnit> unitCodeMap = units.stream().collect(Collectors.toMap(GoodsUnit::getCode, e -> e, (key1, key2) -> key2));

        IntStream.range(NUM_ZERO, deliveryExcelDTOList.size()).forEach(index -> {
            DeliveryExcelDTO deliveryExcelDTO = deliveryExcelDTOList.get(index);
            // 校验商品信息
            Goods goods = goodsMap.get(deliveryExcelDTO.getGoodCode());
            if (goods == null) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODS + NOT_EXIST);
            }
            if (!StatusEnum.ACTIVE.getValue().equals(goods.getStatus())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODS + NOT_ENABLED);
            }
            //校验客户名称
            if (StringUtils.isNotEmpty(deliveryExcelDTO.getCustomerName())) {
                Customer customer = customers.get(deliveryExcelDTO.getCustomerName());
                if (null != customer) {
                    deliveryExcelDTO.setCustomerName(customer.getName());
                    deliveryExcelDTO.setCustomerCode(customer.getCustomerCode());
                } else {
                    deliveryExcelDTO.setCustomerCode("");
                    deliveryExcelDTO.setCustomerName("");
                }
            }
            //承运商
            Carrier carrier = stringCarrierMap.get(deliveryExcelDTO.getCarrierName());
            if (null != carrier) {
                //承运商
                deliveryExcelDTO.setCarrierName(carrier.getName());
                deliveryExcelDTO.setCarrierCode(carrier.getCode());
                deliveryExcelDTO.setCarrierType(carrier.getCarrierType());
                deliveryExcelDTO.setLogisticsCode(carrier.getOuterCode());
            } else {
                deliveryExcelDTO.setCarrierCode("");
            }

            // 获取基本单位
            GoodsUnit basicUnit = unitCodeMap.get(goods.getUnitCode());
            if (basicUnit == null) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + BASICUNIT_NAME + NOT_EXIST);
            }

            // 获取单位转换对象
            GoodsUnitConversion unitConversion = baseGoodsUnitConversion.get(goods.getCode());
            if (null == unitConversion) {
                throw new ServiceException("商品【" + goods.getName() + "】的单位【" + basicUnit.getUnitName() + "】没有维护单位件装量");
            }

            //仓库
            deliveryExcelDTO.setWarehouseCode(warehouse.getCode());
            deliveryExcelDTO.setWarehouseSerialNumber(warehouse.getSerialNumber());
            deliveryExcelDTO.setWarehouseName(warehouse.getName());
            //入住商
            deliveryExcelDTO.setShipmentCode(shipment.getPartyCode());
            deliveryExcelDTO.setShipmentSerialNumber(shipment.getPartyCode());
            deliveryExcelDTO.setShipmentName(shipment.getUserName());

            //发件人信息
            deliveryExcelDTO.setSenderName(carrier.getSenderName());
            deliveryExcelDTO.setSenderMobile(carrier.getSenderMobile());
            deliveryExcelDTO.setSenderProvince(carrier.getProvince());
            deliveryExcelDTO.setSenderCity(carrier.getCity());
            deliveryExcelDTO.setSenderDistrict(carrier.getSenderDistrict());
            deliveryExcelDTO.setSenderDetailAddress(carrier.getAddress());

            //商品信息
            deliveryExcelDTO.setGoodCode(goods.getGoodCode());
            deliveryExcelDTO.setGoodsCode(goods.getCode());
            deliveryExcelDTO.setGoodsName(goods.getName());
            deliveryExcelDTO.setGoodsBarCode(goods.getBarCode());
            deliveryExcelDTO.setSpec(goods.getSpec());
            deliveryExcelDTO.setExpirationQuantity(goods.getQuality());
            deliveryExcelDTO.setGoodsUnit(basicUnit.getCode());
            deliveryExcelDTO.setGoodsUnitName(basicUnit.getUnitName());
            deliveryExcelDTO.setQuantity(new BigDecimal(deliveryExcelDTO.getDamagedQuantity()));
            deliveryExcelDTO.setBasicUnit(basicUnit.getCode());
            deliveryExcelDTO.setBasicUnitName(basicUnit.getUnitName());
            deliveryExcelDTO.setBasicQuantity(unitConversion.getQuantity());
            if (StringUtils.isNotEmpty(deliveryExcelDTO.getPrice())) {
                deliveryExcelDTO.setActualPrice(new BigDecimal(deliveryExcelDTO.getPrice()));
            }
            deliveryExcelDTO.setTotalNum(deliveryExcelDTO.getQuantity().multiply(unitConversion.getQuantity()));
        });

        deliveryCache.initCache(deliveryExcelDTOList);
        return deliveryExcelDTOList;
    }


    private Map<String, GoodsUnitConversion> baseGoodsUnitConversion() {
        List<GoodsUnitConversion> goodsUnitConversions = goodsUnitConversionMapper.selectList(new QueryWrapper<GoodsUnitConversion>().lambda() .eq(GoodsUnitConversion::getGroupCode, UserUtil.getBranchCode()));
        Map<String, GoodsUnitConversion> maps = goodsUnitConversions.stream().collect(Collectors.toMap(GoodsUnitConversion::getGoodsCode, Function.identity(), (key1, key2) -> key2));
        return maps;
    }

    private void validatetype(List<DeliveryExcelDTO> deliveryExcelDTOList) {
        IntStream.range(NUM_ZERO, deliveryExcelDTOList.size()).forEach(index -> {
            if (StringUtils.isEmpty(deliveryExcelDTOList.get(index).getType())) {
                deliveryExcelDTOList.get(index).setTypeName("销售出库");
                deliveryExcelDTOList.get(index).setType(DeliveryTypeEnum.OUT_SALE.getValue());
            } else {
                deliveryExcelDTOList.get(index).setTypeName(deliveryExcelDTOList.get(index).getType());
                deliveryExcelDTOList.get(index).setType(DeliveryTypeEnmTwo.translate(deliveryExcelDTOList.get(index).getType()));
            }
        });
    }

    private void isDeliveringIfStockout(List<DeliveryExcelDTO> deliveryExcelDTOList) {
        IntStream.range(NUM_ZERO, deliveryExcelDTOList.size()).forEach(index -> {
            if (deliveryExcelDTOList.get(index).getIsDeliveringIfStockout().equalsIgnoreCase("是")) {
                deliveryExcelDTOList.get(index).setIsDeliveringIfStockout("1");
            } else if (deliveryExcelDTOList.get(index).getIsDeliveringIfStockout().equalsIgnoreCase("否")) {
                deliveryExcelDTOList.get(index).setIsDeliveringIfStockout("0");
            } else {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + DELIVERING_IF_STOCKOUT + NOT_EXIST);
            }
        });
    }


    /**
     * 校验货主信息
     *
     * @param firstRow
     * @return
     */
    private Shipment validateShipment(DeliveryExcelDTO firstRow) {
//		Shipment shipment = iShipmentService.shipmentBySerial(firstRow.getShipmentSerialNumber());
        Shipment shipment = iShipmentService.shipmentBySerialName(firstRow.getShipmentName());
        if (shipment == null) {
            throw new ServiceException(ENTRANTS_NAME + NOT_EXIST);
        }
        // 校验 货主是否审核通过
        if (!NumConst.NUM_THREE.equals(shipment.getAuthStatus())) {
            throw new ServiceException(ENTRANTS_NAME + UNAPPROVE_NAME);
        }
        return shipment;
    }

    /**
     * 校验客户信息， 如果客户信息为空，则返回一个空的数据，因为客户为非必填项
     *
     * @param firstRow
     * @return
     */
    private Customer validateCustomer(DeliveryExcelDTO firstRow) {
        Customer customer = new Customer();
        if (StringUtils.isNotEmpty(firstRow.getCustomerName())) {
            List<Customer> customers = iCustomerService.list(new QueryWrapper<Customer>().lambda().eq(Customer::getName, firstRow.getCustomerName()));
            if (IterUtil.isEmpty(customers)) {
                throw new ServiceException(CUSTOMER_NAME + NOT_EXIST);
            }
            customer = customers.get(NUM_ZERO);
            if (!NUM_ONE.equals(customer.getStatus())) {
                throw new ServiceException(CUSTOMER_NAME + UNAPPROVE_NAME);
            }
        } else {
            customer.setCode(StrUtil.EMPTY);
            customer.setName(StrUtil.EMPTY);
        }
//        if (StrUtil.isNotBlank(firstRow.getCustomerNumber())) {
//            List<Customer> customers = iCustomerService.list(new QueryWrapper<Customer>().lambda().eq(Customer::getCustomerCode, firstRow.getCustomerNumber()));
//            if (IterUtil.isEmpty(customers)) {
//                throw new ServiceException(CUSTOMER_NAME + NOT_EXIST);
//            }
//            customer = customers.get(NUM_ZERO);
//            // 校验审核状态
//            if (!NUM_ONE.equals(customer.getStatus())) {
//                throw new ServiceException(CUSTOMER_NAME + UNAPPROVE_NAME);
//            }
//        } else {
//            customer.setCode(StrUtil.EMPTY);
//            customer.setName(StrUtil.EMPTY);
//        }
        return customer;
    }


    /**
     * 校验当前用户是否可以导入这些数据的权限
     *
     * @param warehouse
     * @param shipment
     */
    private void validateAuth(Warehouse warehouse, Shipment shipment) {
        // 获取当前用户所操作的仓库、允许操作的用户列表
        List<String> warehouseCodes = UserUtil.getAuthorizeWarehouseCodes();
        List<String> shipmentCodes = UserUtil.getAuthorizeShipmentCodes();
        Map<String, Boolean> shipmentCodeMap = shipmentCodes.stream().collect(Collectors.toMap(e->e, e->true));
        if (IterUtil.isEmpty(warehouseCodes)) {
            throw new ServiceException("用户仓库信息为空");
        }
        if (!warehouseCodes.contains(warehouse.getCode())) {
            throw new ServiceException("当前用户没有导入仓库编号【" + warehouse.getSerialNumber() + "】数据的权限");
        }
        if (!shipmentCodeMap.containsKey(shipment.getPartyCode())) {
            throw new ServiceException("当前用户没有导入入驻商编号【" + shipment.getPartyCode() + "】数据的权限");
        }
    }


    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void deliveryImport() {
        List<DeliveryExcelDTO> deliveryExcelDTOList = deliveryCache.getCache();
        //分组
        Map<String, List<DeliveryExcelDTO>> userInfoMap = deliveryExcelDTOList.parallelStream().collect(
                Collectors.groupingBy(a -> this.format("{0}", a.getSourceCode())));
        List<DeliveryItem> deliveryItemListAll = new ArrayList<>();
        List<Delivery> deliveryListAll = new ArrayList<>();
        String groupCode = UserUtil.getBranchCode();
        String userCode = UserUtil.getCode();
        String userName = partyCache.translate(userCode);
        userInfoMap.entrySet().parallelStream().forEach(r -> {
            List<DeliveryExcelDTO> deliveryExcelDTOS = r.getValue();
            //商品合并
            List<DeliveryExcelDTO> removalList = deliveryExcelDTOS.stream()
                    .collect(Collectors.toMap(goods ->
                            goods.getDeliveryOrderCode() + "_" + goods.getSourcePlatformCode() + "_"
                                    + goods.getCustomerName() + "_" + goods.getCustomerCode() + "_" + goods.getGoodCode() + "_" + goods.getCarrierCode()
                                    + "_" + goods.getCustomerNo() + "_" + goods.getType() + " _" + goods.getPrice() + "_" + goods.getWaybillCode(), a -> a, (o1, o2) -> {
                        o1.setQuantity(o1.getQuantity().add(o2.getQuantity()));
                        o1.setTotalNum(o1.getTotalNum().add(o2.getTotalNum()));
                        return o1;
                    }))
                    .values()
                    .stream().collect(Collectors.toList());
            if (CollUtil.isNotEmpty(removalList)) {
                DeliveryExcelDTO obj = removalList.get(NUM_ZERO);
                Delivery delivery = new Delivery();
                BeanUtils.copyProperties(obj, delivery);
                delivery.setCode(OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.FH, "",UserUtil.getBranchCode()));
                delivery.setGroupCode(groupCode);
                delivery.setIsOmsOrder(NUM_ZERO);
                delivery.setStatus(CollectStatusEnum.NEW.getValue());
                delivery.setCreatedBy(userCode);
                delivery.setCreatedName(partyCache.translate(userCode));
                if (null != obj.getIsDeliveringIfStockout() && obj.getIsDeliveringIfStockout() != "") {
                    delivery.setIsDeliveringIfStockout(Integer.valueOf(obj.getIsDeliveringIfStockout()));
                }
                // 计算明细中有多少不同的商品种数
                Map<String, Boolean> goodsSortMap = new HashMap<>();
                String deliveryCode = delivery.getCode();
                String sourceCode = delivery.getSourceCode();
                List<DeliveryItem> deliveryItemList = removalList.parallelStream().map(item -> {
                    DeliveryItem deliveryItem = new DeliveryItem();
                    BeanUtils.copyProperties(item, deliveryItem);
                    deliveryItem.setCode(IdUtil.fastSimpleUUID());
                    deliveryItem.setDeliveryCode(deliveryCode);
                    deliveryItem.setGroupCode(groupCode);
                    deliveryItem.setSourceItemCode(sourceCode);
                    deliveryItem.setCreatedBy(userCode);
                    deliveryItem.setCreatedName(userName);
                    goodsSortMap.put(item.getGoodsCode(), true);

                    return deliveryItem;
                }).collect(Collectors.toList());
                //明细表数值
                if (null != deliveryItemList && deliveryItemList.size() > 0) {
                    deliveryItemListAll.addAll(deliveryItemList);
                }
                BigDecimal totalNum = deliveryItemList.stream().map(DeliveryItem::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
                delivery.setTotalNum(totalNum);
                // 计算明细中的商品种数
                delivery.setGoodsSortQuantity(new BigDecimal(Integer.toString(goodsSortMap.size())));
                //主表数值
                deliveryListAll.add(delivery);
            }
        });

        /*for (String key : userInfoMap.keySet()) {
            List<DeliveryExcelDTO> deliveryExcelDTOS = userInfoMap.get(key);
            if (CollUtil.isNotEmpty(deliveryExcelDTOS)) {
                Delivery delivery = new Delivery();
                BeanUtils.copyProperties(deliveryExcelDTOS.get(NUM_ZERO), delivery);
                delivery.setCode(OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.FH, ""));
                delivery.setGroupCode(UserUtil.getBranchCode());
                delivery.setIsOmsOrder(NUM_ZERO);
                delivery.setStatus(CollectStatusEnum.NEW.getValue());
                delivery.setCreatedBy(UserUtil.getCode());
                delivery.setCreatedName(partyCache.translate(UserUtil.getCode()));
                if(null != deliveryExcelDTOS.get(NUM_ZERO).getIsDeliveringIfStockout() && deliveryExcelDTOS.get(NUM_ZERO).getIsDeliveringIfStockout() != "") {
                    delivery.setIsDeliveringIfStockout(Integer.valueOf(deliveryExcelDTOS.get(NUM_ZERO).getIsDeliveringIfStockout()));
                }
                // 计算明细中有多少不同的商品种数
                Map<String, Boolean> goodsSortMap = new HashMap<>();

                List<DeliveryItem> deliveryItemList = deliveryExcelDTOS.stream().map(item -> {
                    DeliveryItem deliveryItem = new DeliveryItem();
                    BeanUtils.copyProperties(item, deliveryItem);
                    deliveryItem.setCode(IdUtil.simpleUUID());
                    deliveryItem.setDeliveryCode(delivery.getCode());
                    deliveryItem.setSourceItemCode(delivery.getSourceCode());
                    deliveryItem.setCreatedBy(UserUtil.getCode());
                    deliveryItem.setCreatedName(partyCache.translate(UserUtil.getCode()));
                    goodsSortMap.put(item.getGoodsCode(), true);

                    return deliveryItem;
                }).collect(Collectors.toList());
                //明细表数值
                deliveryItemListAll.addAll(deliveryItemList);

                BigDecimal totalNum = deliveryItemList.stream().map(DeliveryItem::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
                delivery.setTotalNum(totalNum);
                // 计算明细中的商品种数
                delivery.setGoodsSortQuantity(new BigDecimal(Integer.toString(goodsSortMap.size())));
                //主表数值
                deliveryListAll.add(delivery);
            }
        }*/
        deliveryItemListAll.removeIf(Objects::isNull);
        deliveryListAll.removeIf(Objects::isNull);
        iDeliveryItemService.saveBatch(deliveryItemListAll);
        saveBatch(deliveryListAll);
        deliveryCache.removeCache();
    }

    /**
     * 发货单部分确认，生成拣货单
     *
     * @param entity
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void partConfirm(Delivery entity) {
        // 判断单据状态
        Delivery dbEntity = this.getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, entity.getCode()).eq(Delivery::getGroupCode,UserUtil.getBranchCode()));
        if (dbEntity == null) {
            throw new ServiceException("发货单信息不存在");
        }
        DeliveryStatusEnum statusEnum = DeliveryStatusEnum.getEnumByValue(dbEntity.getStatus());
        if (!StrUtil.containsAny(dbEntity.getStatus(), DeliveryStatusEnum.PART_DISTRIBUTION.getValue())) {
            throw new ServiceException("发货单为【" + statusEnum.getDesc() + "】状态无法进行部分确认操作");
        }
        // 查找可分配的分配单信息, 可分配的是指还没有生成拣货单的分配单信息，即拣货单号为空字符串的
        List<DeliveryItemWithDistributionVO> itemWithDistributions = iDeliveryDistributionService.queryWithItemList(entity.getCode(), StrUtil.EMPTY);
        // 判断是否有数据
        if (IterUtil.isEmpty(itemWithDistributions)) {
            throw new ServiceException("没有可用的分配信息，无法生成新的拣货单");
        }

        // 生成拣货单，更新发货单明细、分配单信息
        savePickingAndUpdateDeliveryInfo(dbEntity, itemWithDistributions);

        // 保存发货单备注信息、更新发货单为部分分配状态
        dbEntity.setStatus(DeliveryStatusEnum.PART_DISTRIBUTION.getValue());
        dbEntity.setRemarks(StrUtil.blankToDefault(entity.getRemarks(), StrUtil.EMPTY));
        this.updateByCode(dbEntity);
    }

    /**
     * 出库复核时更新发货单分配数量，如缺货登记后需要调整实际分配数量
     *
     * @param deliveryCode
     * @return
     */
    @Override
    public int updateDistributionQuantityForOutboundRecheck(String deliveryCode,String groupCode) {
        return this.baseMapper.updateDistributionQuantityForOutboundRecheck(deliveryCode,groupCode);
    }

    /**
     * 判断发货单状态为“已分配”，且总发货数量=分配数量
     * 适合只有发货单号时使用
     *
     * @param deliveryCode
     * @return
     */
    @Override
    public boolean isDeliveryAndOutboundAllFinish(String deliveryCode,String groupCode) {
        Delivery delivery = getOne(new QueryWrapper<Delivery>().lambda()
                .eq(Delivery::getCode, deliveryCode)
                .eq(Delivery::getGroupCode,groupCode));
        if (delivery == null) {
            throw new ServiceException("发货单【" + deliveryCode + "】不存在");
        }
        return isDeliveryAndOutboundAllFinish(delivery);
    }

    /**
     * 判断发货单状态为“已分配”，且总发货数量=分配数量
     * 适合已从数据库中获取发货单，已对发货单做过校验的场景使用，减少重复查询发货单
     *
     * @param delivery
     * @return
     */
    @Override
    public boolean isDeliveryAndOutboundAllFinish(Delivery delivery) {
        String groupCode = delivery.getGroupCode();
        boolean finishFlag = false;
        // 获取所有相关已完成的发货单
        List<Outbound> outbounds = iOutboundService.list(new QueryWrapper<Outbound>().lambda()
                .eq(Outbound::getDeliveryCode, delivery.getCode())
                .eq(Outbound::getGroupCode,groupCode)
                .eq(Outbound::getStatus, OutboundStatusEnum.FINISH_OUT.getValue()));
        BigDecimal actualOutQuantity = outbounds.stream().map(Outbound::getOutQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (DeliveryTypeEnum.ALLOCATION.getValue().equals(delivery.getType())){
            if (DeliveryStatusEnum.UN_DELIVERY.getValue().equals(delivery.getStatus())){
                finishFlag = true;
            }
        }
        if (DeliveryStatusEnum.DELIVERIED.getValue().equals(delivery.getStatus())
                && delivery.getDistributionNum().compareTo(actualOutQuantity) == 0) {
            finishFlag = true;
        }
        return finishFlag;
    }


    /**
     * 生成拣货单，更新发货单明细、分配单信息
     *
     * @param dbEntity
     * @param itemWithDistributions
     */
    @Transactional(rollbackFor = {Exception.class})
    public void savePickingAndUpdateDeliveryInfo(Delivery dbEntity, List<DeliveryItemWithDistributionVO> itemWithDistributions) {
        // 生成一个基于数据库实际发货单的临时数据，调整部分数据，用于生成拣货单
        Delivery tempDbEntity = new Delivery();
        BeanUtil.copyProperties(dbEntity, tempDbEntity);
        // 修改DistributionNum
        BigDecimal distributionNum = itemWithDistributions.stream().map(DeliveryItemWithDistributionVO::getDistributionQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
        tempDbEntity.setDistributionNum(distributionNum);

        //2021.09.03  电商部分分配后，点击部分确认，不生成拣货单
        // 根据分配信息生成上架单、上架单明细, 并保存
      /*  Picking picking = generatePicking(tempDbEntity);
        iPickingService.save(picking);
        List<PickingItem> pickingItems = generatePickingItems(picking, itemWithDistributions);
        iPickingItemService.saveBatch(pickingItems);*/

        // 更新发货明细单状态、分配单状态、分配单拣货单号
        List<String> itemCodes = new ArrayList<>();
        List<String> distributionCodes = new ArrayList<>();
        itemWithDistributions.forEach(e -> {
            itemCodes.add(e.getDeliveryItemCode());
            distributionCodes.add(e.getCode());
        });

        iDeliveryItemService.update(new UpdateWrapper<DeliveryItem>().lambda().in(DeliveryItem::getCode, itemCodes)
                .eq(DeliveryItem::getGroupCode,UserUtil.getBranchCode())
                .set(DeliveryItem::getStatus, DeliveryStatusEnum.DISTRIBUTIONED.getValue()));
        //2021.09.03 由于没有生成拣货单，所以分配明细里也不用更新拣货单号
      /*  iDeliveryDistributionService.update(new UpdateWrapper<DeliveryDistribution>().lambda()
                .in(DeliveryDistribution::getCode, distributionCodes).set(DeliveryDistribution::getStatus, DeliveryStatusEnum.DISTRIBUTIONED.getValue())
                .set(DeliveryDistribution::getPickingCode, picking.getCode()));   */
        iDeliveryDistributionService.update(new UpdateWrapper<DeliveryDistribution>().lambda()
                .in(DeliveryDistribution::getCode, distributionCodes).set(DeliveryDistribution::getStatus, DeliveryStatusEnum.DISTRIBUTIONED.getValue()));

    }

    /**
     * 校验Excel里必填项是否有空值
     *
     * @param deliveryExcelDTOList
     */
    private void validateNotBlank(List<DeliveryExcelDTO> deliveryExcelDTOList) {
        for (int index = NUM_ZERO; index < deliveryExcelDTOList.size(); index++) {
            // 校验Excel里必填项是否有空值（必须填写的）
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getWarehouseSerialNumber())) {  //仓库编码
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + WAREHOUSE_SERIAL_NUMBER + NOT_BLANK);
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getShipmentName())) {  //入驻商
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + ENTRANTS_NAME + NOT_BLANK);
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getShopNick())) {  //店铺名称
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SHOP_NICK + NOT_BLANK);
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getSourceCode())) {//商家订单号
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SOURCE_Code + NOT_BLANK);
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getReceiverName())) {  //收货人姓名
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + RECEIVE_NAME + NOT_BLANK);
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getReceiverMobile())) {  //收货人手机号
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + RECEIVE_MOBILE + NOT_BLANK);
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getReceiverProvince())) {  //收件人省
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + RECEIVE_PROVINCE + NOT_BLANK);
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getReceiverCity())) {  //收件人市
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + RECEIVE_CITY + NOT_BLANK);
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getReceiverDistrict())) {  //收件人区县
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + RECEIVE_DISTRICT + NOT_BLANK);
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getReceiverDetailAddress())) {  //收货人详细地址
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + RECEIVE_DETAIL_ADDRESS + NOT_BLANK);
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getGoodCode())) {  //商家商品编码
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOOD_CODE_NAME + NOT_BLANK);
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getDamagedQuantity())) {  //商品数量
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOOD_QUANTITY + NOT_BLANK);
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getCarrierName())) {  //承运商名称
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + CARRIER_NAME + NOT_BLANK);
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getIsDeliveringIfStockout())) {  //缺货是否发货
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + DELIVERING_IF_STOCKOUT + NOT_BLANK);
            }
            //没有填写的赋空用于入库分组
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getDeliveryOrderCode())) {
                deliveryExcelDTOList.get(index).setDeliveryOrderCode("");
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getSourcePlatformCode())) {
                deliveryExcelDTOList.get(index).setSourcePlatformCode("");
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getCustomerName())) {
                deliveryExcelDTOList.get(index).setCustomerName("");
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getCustomerNo())) {
                deliveryExcelDTOList.get(index).setCustomerNo("");
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getType())) {
                deliveryExcelDTOList.get(index).setType("");
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getPrice())) {
                deliveryExcelDTOList.get(index).setPrice("");
            }
            if (StrUtil.isBlank(deliveryExcelDTOList.get(index).getWaybillCode())) {
                deliveryExcelDTOList.get(index).setWaybillCode("");
            }
        }

    }

    public static String format(String value, Object... paras) {
        return MessageFormat.format(value, paras);
    }

    /**
     * 校验客户
     *
     * @param deliveryExcelDTOList
     */
    private Map<String, Customer> validateCustomerName(List<DeliveryExcelDTO> deliveryExcelDTOList) {
        Map<String, Customer> customerss = new HashMap<>();
        List<String> collect = deliveryExcelDTOList.stream().map(DeliveryExcelDTO::getCustomerName).distinct().collect(toList());
        collect.removeAll(Collections.singleton(null));
        List<Customer> customers = iCustomerService.list(new QueryWrapper<Customer>().lambda().eq(Customer::getGroupCode, UserUtil.getBranchCode()));
        Map<String, Customer> maps = customers.stream().collect(Collectors.toMap(Customer::getName, Customer -> Customer));

        IntStream.range(NUM_ZERO, collect.size()).forEach(index -> {
            String name = collect.get(index);
            Customer customer = maps.get(name);
            if (!StrUtil.isBlank(name) && customer == null) {
                throw new ServiceException("客户名称" + name + NOT_EXIST);
            }
            if (customer != null && !CustomerCheckStatusEnum.SUCCESS_CHECK.getCode().equals(customer.getStatus())) {
                throw new ServiceException("客户名称" + name + NOT_ENABLED);
            }
            if (customer != null) {
                customerss.put(name, customer);
            }
        });
        return customerss;
    }

    /**
     * 校验承运商(该入驻商下的承运商)
     *
     * @param deliveryExcelDTOList
     * @param partyCode
     */
    private Map<String, Carrier> validateCarrier(List<DeliveryExcelDTO> deliveryExcelDTOList, String partyCode) {
        Map<String, Carrier> carrierss = new HashMap<>();
        List<Carrier> carrierList = carrierService.list(new QueryWrapper<Carrier>().lambda().eq(Carrier::getShipmentCode, partyCode)
                .eq(Carrier::getGroupCode, UserUtil.getBranchCode()));
        Map<String, Carrier> maps = carrierList.stream().collect(Collectors.toMap(Carrier::getName, Carrier -> Carrier));
        List<String> collect = deliveryExcelDTOList.stream().map(DeliveryExcelDTO::getCarrierName).distinct().collect(toList());
        IntStream.range(NUM_ZERO, collect.size()).forEach(index -> {
            String name = collect.get(index);
            Carrier carrier = maps.get(name);
            if (carrier == null) {
                throw new ServiceException("承运商名称" + name + NOT_EXIST);
            }
            if (!String.valueOf(CustomerCheckStatusEnum.SUCCESS_CHECK.getCode()).equals(carrier.getStatus())) {
                throw new ServiceException("承运商名称" + name + NOT_ENABLED);
            }
            carrierss.put(name, carrier);
        });
        return carrierss;
    }

    public static <T> List<T> getDuplicateElements(Stream<T> stream) {
        return stream
                .collect(Collectors.toMap(e -> e, e -> 1, (a, b) -> a + b)) // 获得元素出现频率的 Map，键为元素，值为元素出现的次数
                .entrySet().stream() // Set<Entry>转换为Stream<Entry>
                .filter(entry -> entry.getValue() > 1) // 过滤出元素出现次数大于 1 的 entry
                .map(entry -> entry.getKey()) // 获得 entry 的键（重复元素）对应的 Stream
                .collect(Collectors.toList()); // 转化为 List
    }

    /**
     * 校验是否有重复数据和不重复的数据
     *
     * @param deliveryExcelDTOList
     */
    private void validateRepeatData(List<DeliveryExcelDTO> deliveryExcelDTOList) {
        Map<String, List<DeliveryExcelDTO>> userInfoMap = deliveryExcelDTOList.stream().collect(
                Collectors.groupingBy(a -> this.format("{0}", a.getSourceCode())));
        if (userInfoMap.size() > 500) {
            throw new ServiceException("导入的订单不能超过500个！");
        }
//		for(String key : userInfoMap.keySet()){
//			List<DeliveryExcelDTO> deliveryExcelDTOS = userInfoMap.get(key);
//			List<String> collect2 = deliveryExcelDTOS.stream().map(DeliveryExcelDTO::getGoodCode).distinct().collect(toList());
//			collect2.removeAll(Collections.singleton(null));
//			if(collect2.size()!=deliveryExcelDTOS.size()){
//                List<String> collect3 = deliveryExcelDTOS.stream().map(DeliveryExcelDTO::getGoodCode).collect(toList());
//                List<String> duplicateElements = getDuplicateElements(collect3.stream());
//                String ss = String.join(",", duplicateElements);
//                throw new ServiceException(key + "订单下商品编码 "+ ss +"重复" );
//			}
//		}
        List<String> collect = deliveryExcelDTOList.stream().map(DeliveryExcelDTO::getWarehouseSerialNumber).distinct().collect(toList());
        collect.removeAll(Collections.singleton(null));
        if (deliveryExcelDTOList.size() != 1 && collect.size() == deliveryExcelDTOList.size()) {
            throw new ServiceException("仓库编码存在多个");
        }
        List<String> collect2 = deliveryExcelDTOList.stream().map(DeliveryExcelDTO::getShipmentName).distinct().collect(toList());
        collect2.removeAll(Collections.singleton(null));
        if (deliveryExcelDTOList.size() != 1 && collect2.size() == deliveryExcelDTOList.size()) {
            throw new ServiceException("入驻商名称存在多个");
        }


        //一个订单下数据不一样
        for (String key : userInfoMap.keySet()) {
            List<DeliveryExcelDTO> deliveryExcelDTOS = userInfoMap.get(key);
            List<DeliveryExcelDTO> deliveryExcelList = deliveryExcelDTOS;
            Stream.iterate(NUM_ZERO, index -> index + NUM_ONE).limit(deliveryExcelDTOS.size()).forEach(index -> {
                Stream.iterate(NUM_ZERO, indexJ -> indexJ + NUM_ONE).limit(deliveryExcelList.size()).forEach(indexJ -> {
                    boolean shopNickFlag = deliveryExcelDTOS.get(index).getShopNick().equals(deliveryExcelList.get(indexJ).getShopNick());
                    if (!shopNickFlag) {
                        throw new ServiceException(key + "订单" + SHOP_NICK + NOT_SAMEB);
                    }
                    boolean sourceCodeFlag = deliveryExcelDTOS.get(index).getSourceCode().equals(deliveryExcelList.get(indexJ).getSourceCode());
                    if (!sourceCodeFlag) {
                        throw new ServiceException(key + "订单" + SOURCE_Code + NOT_SAMEB);
                    }
                    boolean receiverNameFlag = deliveryExcelDTOS.get(index).getReceiverName().equals(deliveryExcelList.get(indexJ).getReceiverName());
                    if (!receiverNameFlag) {
                        throw new ServiceException(key + "订单" + RECEIVE_NAME + NOT_SAMEB);
                    }
                    boolean receiverMobileFlag = deliveryExcelDTOS.get(index).getReceiverMobile().equals(deliveryExcelList.get(indexJ).getReceiverMobile());
                    if (!receiverMobileFlag) {
                        throw new ServiceException(key + "订单" + RECEIVE_MOBILE + NOT_SAMEB);
                    }
                    boolean receiverProvinceFlag = deliveryExcelDTOS.get(index).getReceiverProvince().equals(deliveryExcelList.get(indexJ).getReceiverProvince());
                    if (!receiverProvinceFlag) {
                        throw new ServiceException(key + "订单" + RECEIVE_PROVINCE + NOT_SAMEB);
                    }
                    boolean receiverCityFlag = deliveryExcelDTOS.get(index).getReceiverCity().equals(deliveryExcelList.get(indexJ).getReceiverCity());
                    if (!receiverCityFlag) {
                        throw new ServiceException(key + "订单" + RECEIVE_CITY + NOT_SAMEB);
                    }
                    boolean receiverDistrictFlag = deliveryExcelDTOS.get(index).getReceiverDistrict().equals(deliveryExcelList.get(indexJ).getReceiverDistrict());
                    if (!receiverDistrictFlag) {
                        throw new ServiceException(key + "订单" + RECEIVE_DISTRICT + NOT_SAMEB);
                    }
                    boolean receiverDetailAddressFlag = deliveryExcelDTOS.get(index).getReceiverDetailAddress().equals(deliveryExcelList.get(indexJ).getReceiverDetailAddress());
                    if (!receiverDetailAddressFlag) {
                        throw new ServiceException(key + "订单" + RECEIVE_DETAIL_ADDRESS + NOT_SAMEB);
                    }
                    boolean carrierNameFlag = deliveryExcelDTOS.get(index).getCarrierName().equals(deliveryExcelList.get(indexJ).getCarrierName());
                    if (!carrierNameFlag) {
                        throw new ServiceException(key + "订单" + CARRIER_NAME + NOT_SAMEB);
                    }
                    boolean IsDeliveringIfStockout = deliveryExcelDTOS.get(index).getIsDeliveringIfStockout().equals(deliveryExcelList.get(indexJ).getIsDeliveringIfStockout());
                    if (!IsDeliveringIfStockout) {
                        throw new ServiceException(key + "订单" + DELIVERING_IF_STOCKOUT + NOT_SAMEB);
                    }
                    if (null == deliveryExcelDTOS.get(index).getCustomerName()) {
                        deliveryExcelDTOS.get(index).setCustomerName("");
                    }
                    if (null == deliveryExcelList.get(indexJ).getCustomerName()) {
                        deliveryExcelList.get(indexJ).setCustomerName("");
                    }
                    if (null == deliveryExcelDTOS.get(index).getType()) {
                        deliveryExcelDTOS.get(index).setType("");
                    }
                    if (null == deliveryExcelList.get(indexJ).getType()) {
                        deliveryExcelList.get(indexJ).setType("");
                    }
                    boolean customerNameFlag = deliveryExcelDTOS.get(index).getCustomerName().equals(deliveryExcelList.get(indexJ).getCustomerName());
                    if (!customerNameFlag) {
                        throw new ServiceException(key + "订单" + CUSTOMER_NAME + NOT_SAME);
                    }
                    boolean typeFlag = deliveryExcelDTOS.get(index).getType().equals(deliveryExcelList.get(indexJ).getType());
                    if (!typeFlag) {
                        throw new ServiceException(key + "订单" + ORDERTYPE + NOT_SAME);
                    }
//					if (!index.equals(indexJ)) {
//						throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + AND_NAME + TIP_PREFIX + (indexJ + NUM_THREE) + LINE + NOT_SAME);
//					}
                });
            });

        }

    }

    /**
     * 校验仓库、入驻商、客户信息是否一致，因为只允许导入一个发货单，所以仓库、入驻商、客户信息都是一样的
     *
     * @param deliveryExcelDTOList
     */
    private void validateSameInfo(List<DeliveryExcelDTO> deliveryExcelDTOList) {
        // 仓库编码、货主编码、供应商/客户编码、来源单号、订单类型 必须保持一致，否则就视为非同一单的
        List<DeliveryExcelDTO> groupList = deliveryExcelDTOList.parallelStream().collect(Collectors.collectingAndThen(Collectors.toCollection(
                () -> new TreeSet<>(Comparator.comparing(e -> e.getWarehouseSerialNumber() + "#" + e.getShipmentSerialNumber()
                        + "#" + e.getCustomerCode()))),
                ArrayList<DeliveryExcelDTO>::new));
        if (groupList.size() > NUM_ONE) {
            throw new ServiceException("仓库编码、货主编码、客户编码必须一致");
        }
    }

    /**
     * 校验货主是否相同 只允许单货主导入
     *
     * @param deliveryExcelDTOList
     */
    private void validateSameShipment(List<DeliveryExcelDTO> deliveryExcelDTOList) {
        deliveryExcelDTOList.forEach(item -> {
            deliveryExcelDTOList.forEach(item1 -> {
                if (!item.getShipmentSerialNumber().equals(item1.getShipmentSerialNumber())) {
                    throw new ServiceException(EXIST + SHIPMENT_SERIAL_NUMBER + NOT_SAME + DATA_NAME);
                }
            });
        });
    }

    /**
     * 校验仓库是否启用并且开启库位管理
     *
     * @param firstRow
     */
    private Warehouse validateWarehouseLocationManagement(DeliveryExcelDTO firstRow) {
        List<Warehouse> warehouses = iWarehouseService.list(new QueryWrapper<Warehouse>().lambda().eq(Warehouse::getSerialNumber, firstRow.getWarehouseSerialNumber()).eq(Warehouse::getGroupCode, UserUtil.getBranchCode()));
        if (IterUtil.isEmpty(warehouses)) {
            throw new ServiceException(WAREHOUSE + NOT_EXIST);
        }

        Warehouse warehouse = warehouses.get(NUM_ZERO);
        // 校验仓库是否启用
        if (!StatusEnum.ACTIVE.getValue().equals(warehouse.getStatus())) {
            throw new ServiceException(WAREHOUSE + NOT_ENABLED);
        }
        // 校验仓库是否启用库位管理
        if (!NUM_ONE.equals(warehouse.getLocationManagement())) {
            throw new ServiceException(WAREHOUSE + NOT_LOCATION_MANAGEMENT);
        }

        return warehouse;
    }

    /**
     * 发货单分配量为0则更新状态为“待分配”
     *
     * @param deliveryCode
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void updateDeliveryStatusIfEmptyDistribution(String deliveryCode,String groupCode) {
        // 判断发货单分配数量是否已小于等于0, 分配数量等于0则将分配单状态设为“待分配”
        Delivery delivery = getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, deliveryCode)
                .eq(Delivery::getGroupCode, groupCode)
                .le(Delivery::getDistributionNum, BigDecimal.ZERO));
        if (delivery != null) {
            update(new UpdateWrapper<Delivery>().lambda().eq(Delivery::getCode, deliveryCode)
                    .eq(Delivery::getGroupCode, groupCode)
                    .set(Delivery::getStatus, DeliveryStatusEnum.UN_DISTRIBUTION.getValue()));
        }
    }

    /**
     * 订单处理生成波次单号、拣货单、拣货单明细
     *
     * @param deliveryDTO
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Map<String, Object> saveWave(DeliveryDTO deliveryDTO) {
        deliveryDTO.setGroupCode(UserUtil.getBranchCode());
        deliveryDTO.setType("0");
        setQueryWhereSql(deliveryDTO);
        //根据查询条件判断是否复核生成波次
        if (null == deliveryDTO.getStatus() || !DeliveryStatusEnum.DISTRIBUTIONED.getValue().equals(deliveryDTO.getStatus())) {
            long count = this.queryNotAllocatedCount(deliveryDTO);
            if (count > NUM_ZERO) {
                throw new ServiceException(HAVE + count + ITEM + ORDER + NOT_BE + DISTRIBUTIONED + STATUS_NAME + COMMA + NOT + CREATE + WAVE);
            }
        }
        String uuid = IdUtil.simpleUUID();
        deliveryDTO.setTemporaryCode(uuid);
        //根据条件更新发货单生成波次中状态
        baseMapper.updateDeliveryWaveStatus(deliveryDTO);
        // 组装消息并发送至mq
        Map<String, Object> message = Maps.newHashMap();
        message.put(MAX_ORDER_QUANTITY, deliveryDTO.getMaxOrderQuantity());
        message.put(TEMPORARY_CODE, uuid);
        return message;
    }

    /**
     * 订单处理根据查询内容生成查询条件sql语句
     *
     * @param deliveryDTO
     */
    private void setQueryWhereSql(DeliveryDTO deliveryDTO) {
        // 判断是否有波次的商品、数量条件，没有则只使用普通查询
        String goodCodesStr = deliveryDTO.getGoodCodes();
        String goodsNumsStr = deliveryDTO.getGoodsNums();
        if (StrUtil.isBlank(goodCodesStr) && StrUtil.isBlank(goodsNumsStr)) {
            return;
        }
        String[] goodCodes = goodCodesStr.split(",");
        String[] goodsNums = goodsNumsStr.split(",");
        // 校验商品信息条件
        validateGoodsConditions(goodCodes, goodsNums);
        // 拼接子查询逻辑
        String waveSql = makeWaveSubSql(goodCodes, goodsNums, deliveryDTO.getContainGoods());
        //
        deliveryDTO.setWaveSql(waveSql);
    }

    /**
     * 查询不是已分配状态的记录数
     *
     * @param deliveryDTO
     * @return
     */
    private long queryNotAllocatedCount(DeliveryDTO deliveryDTO) {

        return baseMapper.queryNotAllocatedCount(deliveryDTO);
    }


    /**
     * 解除发货单与波次的关系
     *
     * @param delivery
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int releaseRelation(Delivery delivery) {
        delivery = this.getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, delivery.getCode()).eq(Delivery::getGroupCode,UserUtil.getBranchCode()));
        if (delivery.getPickNum().compareTo(BigDecimal.ZERO) > 0) {
            throw new ServiceException("该订单已拣货，无法解除波次关系！！！");
        }
        //解决发货单与波次单号的关系
        this.update(new UpdateWrapper<Delivery>().lambda().eq(Delivery::getCode, delivery.getCode()).eq(Delivery::getGroupCode, UserUtil.getBranchCode())
                .set(Delivery::getWavePickingCode, StrUtil.EMPTY)
                .set(Delivery::getStatus, DeliveryStatusEnum.DISTRIBUTIONED.getValue()));
        //如果拣货单只有一个订单，则应将拣货单从列表中删除
        List<DeliveryDistribution> distributions = iDeliveryDistributionService.list(new QueryWrapper<DeliveryDistribution>().lambda()
                .eq(DeliveryDistribution::getDeliveryCode, delivery.getCode()).eq(DeliveryDistribution::getDeleted, NUM_ZERO).eq(DeliveryDistribution::getGroupCode,UserUtil.getBranchCode()));
        //更新拣货单与拣货单明细的分配数量和计划数量
        for (DeliveryDistribution distribution : distributions) {
            iPickingRegisterService.update(new UpdateWrapper<PickingRegister>().lambda().eq(PickingRegister::getWavePickingCode, distribution.getWavePickingCode())
                    .eq(PickingRegister::getGroupCode,UserUtil.getBranchCode())
                    .eq(PickingRegister::getInventoryCode, distribution.getInventoryCode())
                    .setSql(SqlConst.PLAN_QUANTITY_EQUALS_PLAN_QUANTITY + distribution.getDistributionQuantity().negate())
                    .setSql(SqlConst.DISTRIBUTION_QUANTITY_EQUALS_DISTRIBUTION_QUANTITY + distribution.getDistributionQuantity().negate()));
            iPickingItemService.update(new UpdateWrapper<PickingItem>().lambda().eq(PickingItem::getWavePickingCode, distribution.getWavePickingCode())
                    .eq(PickingItem::getGroupCode,UserUtil.getBranchCode())
                    .eq(PickingItem::getInventoryCode, distribution.getInventoryCode())
                    .setSql(SqlConst.PLAN_QUANTITY_EQUALS_PLAN_QUANTITY + distribution.getDistributionQuantity().negate())
                    .setSql(SqlConst.DISTRIBUTION_QUANTITY_EQUALS_DISTRIBUTION_QUANTITY + distribution.getDistributionQuantity().negate()));
            //PickingItem pickingItem = iPickingItemService.getOne(new QueryWrapper<PickingItem>().lambda().eq(PickingItem::getWavePickingCode, distribution.getWavePickingCode())
            //        .eq(PickingItem::getInventoryCode, distribution.getInventoryCode()));
            iPickingService.update(new UpdateWrapper<Picking>().lambda().eq(Picking::getWavePickingCode, distribution.getWavePickingCode())
                    .eq(Picking::getGroupCode,UserUtil.getBranchCode())
                    .setSql(SqlConst.NOT_PICKING_QUANTITY_EQUALS_NOT_PICKING_QUANTITY + distribution.getDistributionQuantity().negate())
                    .setSql(SqlConst.PLAN_QUANTITY_EQUALS_PLAN_QUANTITY + distribution.getDistributionQuantity().negate()));
            //更新临时库存
            iInventoryService.stockoutInventory(distribution.getInventoryCode(), distribution.getDistributionQuantity(), StockoutTypeEnum.PICKING.getValue(), distribution.getPickingCode());
            distribution.setWavePickingCode(StrUtil.EMPTY);
        }

        iDeliveryDistributionService.update(new UpdateWrapper<DeliveryDistribution>().lambda()
                .eq(DeliveryDistribution::getGroupCode,UserUtil.getBranchCode())
                .eq(DeliveryDistribution::getDeliveryCode, delivery.getCode())
                .set(DeliveryDistribution::getWavePickingCode, StrUtil.EMPTY));

        this.iDeliveryItemService.update(new UpdateWrapper<DeliveryItem>().lambda()
                .eq(DeliveryItem::getGroupCode,UserUtil.getBranchCode())
                .eq(DeliveryItem::getDeliveryCode, delivery.getCode())
                .set(DeliveryItem::getWavePickingCode, StrUtil.EMPTY));

        return NUM_ONE;
    }

    /**
     * 发货单 打印面单
     *
     * @param deliveryCodes
     * @return
     */
    @Override
    public List<DeliveryVO> printExpress(List<String> deliveryCodes) {
        DeliveryDTO deliveryDTO = new DeliveryDTO();
        deliveryDTO.setDeliveryCodes(deliveryCodes);
        deliveryDTO.setGroupCode(UserUtil.getBranchCode());
        List<DeliveryVO> deliveryList = baseMapper.queryAllList(deliveryDTO);
        long count = deliveryList.stream().filter(item -> DeliveryStatusEnum.VOID.getValue().equals(item.getStatus())).count();
        if (count > NUM_ZERO) {
            throw new ServiceException(HAVE + count + ITEM + ORDER + CANCELED);
        }
        long count1 = deliveryList.stream().filter(item -> ObjectUtil.isEmpty(item.getWaybillCode())).count();
        if (count1 > NUM_ZERO) {
            throw new ServiceException(HAVE + count1 + ITEM + ORDER + NO_WAYBILL_CODE);
        }
        long countLogistic = deliveryList.stream().filter(item -> "自提".equals(item.getWaybillCode())).count();
        if (countLogistic > NUM_ZERO) {
            throw new ServiceException("承运商类型是物流，无法打印面单");
        }
        // 校验是否都是同一承运商
        Set<String> carrierCodeSet  = deliveryList.stream().map(DeliveryVO::getCarrierCode).collect(Collectors.toSet());
        if (IterUtil.isEmpty(carrierCodeSet)) {
            throw new ServiceException("找不到承运商");
        }
        if (carrierCodeSet.size() > NUM_ONE) {
            throw new ServiceException(MUST + CHOOSE + SAME + LOGISTICS_NAME + DELIVERY);
        }
        DeliveryItemDTO deliveryItemDTO = new DeliveryItemDTO();
        deliveryItemDTO.setPageNum(1);
        deliveryItemDTO.setPageSize(9999);
        deliveryItemDTO.setGroupCode(UserUtil.getBranchCode());


        Map<String, Integer> countMap = new HashMap<>();
        //由于获取面单的时候已经对承运商和店铺进行过校验，这里不在进行判断
        Carrier carrier = carrierCache.getCarrier(carrierCodeSet.iterator().next());
        Shop shop = shopCache.getShop(carrier.getShopCode());
        String token = shop.getAccessToken();
        deliveryList.forEach(r -> {
            r.setPlatformType(carrier.getPlatformType());
            Integer c = MapUtil.getInt(countMap, r.getWavePickingCode());
            if (null == c) {
                c = baseMapper.selectCount(new LambdaQueryWrapper<Delivery>().eq(Delivery::getWavePickingCode, r.getWavePickingCode()).eq(Delivery::getGroupCode, UserUtil.getBranchCode()));
                countMap.put(r.getWavePickingCode(), c);
            }
            r.setOrderQuantity(c);
            deliveryItemDTO.setDeliveryCode(r.getCode());
            List<DeliveryItemVO> deliveryItemVOList = this.iDeliveryItemService.queryList(deliveryItemDTO);
            r.setDeliveryItemVOList(deliveryItemVOList);
            r.setSourcePlatformName(SourcePlatformUtils.translate(r.getSourcePlatformCode()));

            String printDataStr = r.getPrintData();
            if(StrUtil.equalsAny(r.getSourcePlatformCode(),"DY","DYXD")&&StrUtil.isNotBlank(printDataStr)&&StrUtil.isNotBlank(token)) {
                String p = DouyinUtil.getPrintParam(token);
                Map<String, String> printData = JSONUtil.toBean(printDataStr, Map.class);
                printData.put("params", p);
                printData.put("templateURL", carrier.getStandardTemplateUrl());
                r.setPrintData(JSONUtil.toJsonStr(printData));
            }

        });
        log.info("波次号【{}】的打印信息为{}",deliveryList.get(NUM_ZERO).getWavePickingCode(),JSONUtil.toJsonStr(deliveryList));
        //如果订单生成波次，则根据订单排序
        long count2 = deliveryList.stream().filter(item -> ObjectUtil.isNotEmpty(item.getOrderNum())).count();
        if (count2 == deliveryList.size()) {
            return deliveryList.stream().sorted(Comparator.comparing(DeliveryVO::getOrderNum)).collect(toList());
        }
        return deliveryList;
    }

    /**
     * 批量拣货完成，更新发货单状态
     *
     * @param pickingCode
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void updateStatusByPickingCode(String pickingCode) {
        Picking picking = iPickingService.getOne(new QueryWrapper<Picking>().lambda().eq(Picking::getCode, pickingCode));
        //根据波次号更新发货单的状态
        this.update(new UpdateWrapper<Delivery>().lambda().eq(Delivery::getWavePickingCode, picking.getWavePickingCode())
                .apply("distribution_num = pick_num")
                .set(Delivery::getStatus, DeliveryStatusEnum.UN_RECHECK.getValue()));
    }

    /**
     * 与queryList方法一致，但没有分页、排序限制
     *
     * @param deliveryDTO
     * @return
     */
    @Override
    public List<DeliveryVO> queryAllList(DeliveryDTO deliveryDTO) {
        deliveryDTO.setGroupCode(UserUtil.getBranchCode());
        return baseMapper.queryAllList(deliveryDTO);
    }

    /**
     * 批量生成拣货单
     *
     * @param codes
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Map<String, Object> batchGeneratePicking(List<String> codes) {
        String branchCode = UserUtil.getBranchCode();
        final String noExistDelivery = "订单信息不存在，无法生成拣货单";
        List<String> errorInfos = new ArrayList<>();
        // 返回结果
        Map<String, Object> result = new HashMap<>();
        // 成功次数
        int successNum = 0;
        // 查询所有发货单（暂不考虑已删除的，那样会影响效率）
        List<Delivery> deliveries = this.list(new QueryWrapper<Delivery>().lambda().in(Delivery::getCode, codes).eq(Delivery::getGroupCode,branchCode));
        //校验发货单状态
        List<Delivery> notDistributionedDeliveries = deliveries.stream().filter(e -> !(e.getStatus().equals(DeliveryStatusEnum.DISTRIBUTIONED.getValue())
                || e.getStatus().equals(DeliveryStatusEnum.PART_DISTRIBUTION.getValue())))
                .collect(Collectors.toList());
        if (IterUtil.isNotEmpty(notDistributionedDeliveries)) {
            throw new ServiceException("发货单状态不全为已分配或部分分配！！！");
        }
        if (IterUtil.isEmpty(deliveries)) {
            codes.forEach(e -> {
                errorInfos.add(e + noExistDelivery);
            });
            result.put("success", successNum);
            result.put("errorInfos", errorInfos);
            return result;
        }

        // 生成拣货单、明细信息
        List<Picking> pickings = generatePickingAndItems(deliveries, errorInfos);
        // 批量保存拣货单、明细信息、待确认单信息
        iPickingService.batchSavePickingAndItemAndRegisters(pickings);
        //更新订单状态为待拣货
        pickings.forEach(r -> {
            this.update(new UpdateWrapper<Delivery>().lambda()
                    .eq(Delivery::getCode, r.getDeliveryCode())
                    .eq(Delivery::getGroupCode,branchCode)
                    .set(Delivery::getStatus, DeliveryStatusEnum.UN_PICKING.getValue())
                    .set(Delivery::getWavePickingCode, r.getWavePickingCode())
                    .set(Delivery::getOrderNum,BigDecimal.ONE));
            iDeliveryItemService.update(new LambdaUpdateWrapper<DeliveryItem>()
                    .eq(DeliveryItem::getGroupCode,branchCode)
                    .eq(DeliveryItem::getDeliveryCode, r.getDeliveryCode())
                    .set(DeliveryItem::getStatus, DeliveryStatusEnum.UN_PICKING.getValue())
                    .set(DeliveryItem::getWavePickingCode, r.getWavePickingCode()));
            iDeliveryDistributionService.update(new LambdaUpdateWrapper<DeliveryDistribution>()
                    .eq(DeliveryDistribution::getGroupCode,branchCode)
                    .eq(DeliveryDistribution::getDeliveryCode, r.getDeliveryCode())
                    .set(DeliveryDistribution::getStatus, DeliveryStatusEnum.UN_PICKING.getValue())
                    .set(DeliveryDistribution::getWavePickingCode, r.getWavePickingCode()));

        });

        result.put("success", pickings.size());
        result.put("errorInfos", errorInfos);
        return result;
    }

    /**
     * 根据发货单生成拣货单、拣货明细
     *
     * @param deliveries
     * @param errorInfos
     * @return
     */
    private List<Picking> generatePickingAndItems(List<Delivery> deliveries, List<String> errorInfos) {
        String groupCode = deliveries.get(NUM_ZERO).getGroupCode();
        List<Picking> pickings = new ArrayList<>();
        final String deliveryStatusError = "订单信息不是【已分配】状态，无法生成拣货单";


        List<String> deliveryCodes = deliveries.stream().map(Delivery::getCode).collect(Collectors.toList());
        if (IterUtil.isNotEmpty(deliveryCodes)) {
            // 获取发货单分配信息
            List<DeliveryDistribution> distributionList = iDeliveryDistributionService.list(new QueryWrapper<DeliveryDistribution>().lambda()
                    .eq(DeliveryDistribution::getGroupCode,groupCode)
                    .in(DeliveryDistribution::getDeliveryCode, deliveryCodes));
            Map<String, List<DeliveryDistribution>> distributionMap = distributionList.stream().collect(Collectors.groupingBy(DeliveryDistribution::getDeliveryCode));

            List<DeliveryItem> deliveryItemList = iDeliveryItemService.list(new UpdateWrapper<DeliveryItem>().lambda()
                    .eq(DeliveryItem::getGroupCode,groupCode)
                    .in(DeliveryItem::getDeliveryCode, deliveryCodes));
            Map<String, List<DeliveryItem>> itemMap = deliveryItemList.stream().collect(Collectors.groupingBy(DeliveryItem::getDeliveryCode));


            for (Delivery delivery : deliveries) {
                // 判断发货单状态
                if (!(DeliveryStatusEnum.DISTRIBUTIONED.getValue().equals(delivery.getStatus()) || DeliveryStatusEnum.PART_DISTRIBUTION.getValue().equals(delivery.getStatus()))) {
                    errorInfos.add(delivery.getCode() + deliveryStatusError);
                    continue;
                }
                //生成波次拣货单号
                String wavePickingCode = OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.BC, delivery.getShipmentCode(),groupCode);
                // 生成拣货单主单信息
                Picking picking = generatePicking(delivery);
                picking.setWavePickingCode(wavePickingCode);
                // 获取发货单分配信息
                List<DeliveryDistribution> distributions = distributionMap.get(delivery.getCode());
                // 根据发货单分配信息，生成拣货明细
                List<PickingItem> pickingItems = generatePickingItemsByDistributions(picking, distributions, itemMap.get(delivery.getCode()), delivery.getCode());
                // 放进主单中
                picking.setPickingItemList(pickingItems);

                pickings.add(picking);
            }
        }

        return pickings;
    }


    /**
     * 波次查询总数
     *
     * @param deliveryDTO
     * @return
     */
    @Override
    public DeliveryVO queryWaveListSum(DeliveryDTO deliveryDTO) {
        deliveryDTO.setType("0");
        // 判断是否有波次的商品、数量条件，没有则使用普通查询或关联明细
        String goodCodesStr = deliveryDTO.getGoodCodes();
        String goodsNumsStr = deliveryDTO.getGoodsNums();
        if (StrUtil.isBlank(goodCodesStr) && StrUtil.isBlank(goodsNumsStr)) {
            // 如果商品查询字段不为空，则需要关联明细表查询订单
            if (StrUtil.isNotBlank(deliveryDTO.getNormalGoodCodes())) {
                return this.queryWaveListJoinItemSum(deliveryDTO);
            }

        } else {

            // 爆品查询
            String[] goodCodes = goodCodesStr.split(",");
            String[] goodsNums = goodsNumsStr.split(",");
            // 校验商品信息条件
            validateGoodsConditions(goodCodes, goodsNums);

            // 拼接子查询逻辑
            String waveSql = makeWaveSubSql(goodCodes, goodsNums, deliveryDTO.getContainGoods());
            deliveryDTO.setWaveSql(waveSql);
        }

        if (IterUtil.isEmpty(deliveryDTO.getShipmentCodes())) {
            return null;
        }

        return this.queryWaveListTotal(deliveryDTO);
       /* StringBuilder sb = new StringBuilder();
        sb.append("SELECT ");
        deliveryDTO.getShipmentCodes().forEach(e -> {
            sb.append("(SELECT count(*) AS total FROM `wms_delivery` a  WHERE 1=1 and (carrier_type='logistics' or waybill_code<>'') ");

            if (ObjectUtil.isNotEmpty(deliveryDTO.getStatus())) {
                sb.append(" and `status` ='").append(deliveryDTO.getStatus()).append("' ");
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getType())) {
                sb.append("  and `type` ='").append(deliveryDTO.getType()).append("' ");
            }

            if (ObjectUtil.isNotEmpty(deliveryDTO.getIsCancel())) {
                sb.append("  and `is_cancel` = ").append(deliveryDTO.getIsCancel());
            }

            sb.append(" and `is_deleted` =  0");

            if (StrUtil.isNotEmpty(deliveryDTO.getWarehouseCode())) {
                sb.append(" and `warehouse_code` ='").append(deliveryDTO.getWarehouseCode()).append("' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getGroupCode())) {
                sb.append(" and `group_code` ='").append(deliveryDTO.getGroupCode()).append("' ");
            }
            sb.append(" and `shipment_code` =  '").append(e).append("' ");


            if (StrUtil.isNotEmpty(deliveryDTO.getCode())) {
                sb.append("  and `code` like '%").append(deliveryDTO.getCode()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getSourceCode())) {
                sb.append(" and `source_code` ='").append(deliveryDTO.getSourceCode()).append("' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getShipmentName())) {
                sb.append("  and `shipment_name` like'%").append(deliveryDTO.getShipmentName()).append("%' ");
            }

            if (StrUtil.isNotEmpty(deliveryDTO.getShopNick())) {
                sb.append(" and `shop_nick` like'%").append(deliveryDTO.getShopNick()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getIsvCode())) {
                sb.append(" and `isv_code` like'%").append(deliveryDTO.getIsvCode()).append("%' ");
            }

            if (StrUtil.isNotEmpty(deliveryDTO.getDeliveryOrderCode())) {
                sb.append(" and `delivery_order_code` like'%").append(deliveryDTO.getDeliveryOrderCode()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getLogisticsCode())) {
                sb.append(" and `logistics_code` like'%").append(deliveryDTO.getLogisticsCode()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getWaybillCode())) {
                sb.append(" and `waybill_code` like'%").append(deliveryDTO.getWaybillCode()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getWavePickingCode())) {
                if (deliveryDTO.isWavePickingCodeFullQuery()){
                    sb.append(" and `wave_picking_code` ='").append(deliveryDTO.getWavePickingCode()).append("' ");
                }else {
                    sb.append(" and `wave_picking_code` like'%").append(deliveryDTO.getWavePickingCode()).append("%' ");
                }
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getCarrierName())) {
                sb.append(" and `carrier_name` like'%").append(deliveryDTO.getCarrierName()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getCarrierCode())) {
                sb.append(" and `carrier_code` ='").append(deliveryDTO.getCarrierCode()).append("' ");
            }



            if (ObjectUtil.isNotEmpty(deliveryDTO.getGoodsSortQuantityStart()) && ObjectUtil.isNotEmpty(deliveryDTO.getGoodsSortQuantityEnd())) {
                sb.append(" and `goods_sort_quantity` between ").append(deliveryDTO.getGoodsSortQuantityStart()).append(" and ").append(deliveryDTO.getGoodsSortQuantityEnd());
            }

            if (ObjectUtil.isNotEmpty(deliveryDTO.getGmtCreatedStart()) && ObjectUtil.isNotEmpty(deliveryDTO.getGmtCreatedEnd())) {
                sb.append(" and `gmt_created` between '").append(deliveryDTO.getGmtCreatedStart()).append("' and '").append(deliveryDTO.getGmtCreatedEnd()).append("' ");
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getGmtModifiedStart()) && ObjectUtil.isNotEmpty(deliveryDTO.getGmtModifiedEnd())) {
                sb.append(" and `gmt_modified` between '").append(deliveryDTO.getGmtModifiedStart()).append("' and '").append(deliveryDTO.getGmtModifiedEnd()).append("' ");
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getTotalNumStart()) && ObjectUtil.isNotEmpty(deliveryDTO.getTotalNumEnd())) {
                sb.append(" and `total_num` between '").append(deliveryDTO.getTotalNumStart()).append("' and '").append(deliveryDTO.getTotalNumEnd()).append("' ");
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getDistributionNumStart()) && ObjectUtil.isNotEmpty(deliveryDTO.getDistributionNumEnd())) {
                sb.append(" and `distribution_num` between '").append(deliveryDTO.getDistributionNumStart()).append("' and '").append(deliveryDTO.getDistributionNumEnd()).append("' ");
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getPickNumStart()) && ObjectUtil.isNotEmpty(deliveryDTO.getPickNumEnd())) {
                sb.append(" and `pick_num` between  ").append(deliveryDTO.getPickNumStart()).append("  and  ").append(deliveryDTO.getPickNumEnd());
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getSendNumStart()) && ObjectUtil.isNotEmpty(deliveryDTO.getSendNumEnd())) {
                sb.append(" and `send_num` between  ").append(deliveryDTO.getSendNumStart()).append("  and  ").append(deliveryDTO.getSendNumEnd());
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getPickNum())) {
                sb.append(" and `pick_num` ='").append(deliveryDTO.getPickNum());
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getSendNum())) {
                sb.append(" and `send_num` ='").append(deliveryDTO.getSendNum());
            }

            if (StrUtil.isNotEmpty(deliveryDTO.getOrderTag())) {
                sb.append(" and `order_tag` ='").append(deliveryDTO.getOrderTag()).append("' ");
            }

            if (StrUtil.isNotEmpty(deliveryDTO.getReceiverName())) {
                sb.append(" and `receiver_name` like'%").append(deliveryDTO.getReceiverName()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getReceiverMobile())) {
                sb.append(" and `receiver_mobile` like'%").append(deliveryDTO.getReceiverMobile()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getSenderDetailAddress())) {
                sb.append(" and `sender_detail_address` like'%").append(deliveryDTO.getSenderDetailAddress()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getReceiverDetailAddress())) {
                sb.append("  and CONCAT(IFNULL(receiver_province,''),IFNULL(receiver_city,''),IFNULL(receiver_district,''),IFNULL(receiver_detail_address,'')) LIKE '%").append(deliveryDTO.getReceiverDetailAddress()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getShipmentName())) {
                sb.append("  and `shipment_name` like'%").append(deliveryDTO.getShipmentName()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getCarrierType())) {
                sb.append("  and `carrier_type` like'%").append(deliveryDTO.getCarrierType()).append("%' ");
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getSourceType())) {
                sb.append("  and `source_type` ='").append(deliveryDTO.getSourceType()).append("' ");
            }
            if (ObjectUtil.isNotEmpty(deliveryDTO.getWaybillPrintCount())) {
                sb.append(" and `waybill_print_count` = ").append(deliveryDTO.getWaybillPrintCount());
            }
            if (StrUtil.isNotEmpty(deliveryDTO.getWaveSql())) {
                sb.append(deliveryDTO.getWaveSql());
            }
            sb.append(" ) +");
        });
        sb.replace(sb.length() - 1, sb.length(), "");
        sb.append(" AS total ");

        return this.baseMapper.queryListSum(sb.toString());*/
    }

    /**
     * 波次查询
     *
     * @param deliveryDTO
     * @return
     */
    @Override
    public List<DeliveryVO> queryWaveList(DeliveryDTO deliveryDTO) {
        // 判断是否有波次的商品、数量条件，没有则使用普通查询或关联明细
        String goodCodesStr = deliveryDTO.getGoodCodes();
        String goodsNumsStr = deliveryDTO.getGoodsNums();
        deliveryDTO.setType("0");
        if (StrUtil.isBlank(goodCodesStr) && StrUtil.isBlank(goodsNumsStr)) {
            // 如果商品查询字段不为空，则需要关联明细表查询订单
            if (StrUtil.isNotBlank(deliveryDTO.getNormalGoodCodes())) {
                return this.queryWaveListJoinItem(deliveryDTO);
            }

        } else {


            // 爆品查询
            String[] goodCodes = goodCodesStr.split(",");
            String[] goodsNums = goodsNumsStr.split(",");
            // 校验商品信息条件
            validateGoodsConditions(goodCodes, goodsNums);

            // 拼接子查询逻辑
            String waveSql = makeWaveSubSql(goodCodes, goodsNums, deliveryDTO.getContainGoods());
            deliveryDTO.setWaveSql(waveSql);

        }
        //排序字段名需要驼峰转数据库下划线类型字段名
        if (StringUtils.isNotEmpty(deliveryDTO.getSortField())) {
            deliveryDTO.setSortField(CommonUtil.camel2Underline(deliveryDTO.getSortField()));
        }
        List<DeliveryVO> deliveryVOS = baseMapper.queryWaveList(deliveryDTO);
        return deliveryVOS.stream().peek(info -> {
            DeliveryStatusEnum enumByCode = DeliveryStatusEnum.getEnumByValue(info.getStatus());
            if (null != enumByCode) {
                info.setStatusText(enumByCode.getDesc());
            }
            DeliveryTypeEnum billType = DeliveryTypeEnum.getEnumByValue(info.getType());
            if (null != billType) {
                info.setTypeText(billType.getDesc());
            }
            String logisticsName = LogisticsUtils.translate(info.getLogisticsCode());
            if (StrUtil.isEmpty(logisticsName)) {
                info.setLogisticsName(logisticsName);
            }
            String carrierTypeName = CarrierTypeEnum.translate(info.getCarrierType());
            if (StrUtil.isNotEmpty(carrierTypeName)) {
                info.setCarrierTypeName(carrierTypeName);
            }
            // 翻译订单来源类型
            info.setSourceTypeText(DeliverySourceTypeEnum.translate(info.getSourceType()));
            //翻译订单来源平台名称
            info.setSourcePlatformName(SourcePlatformUtils.translate(info.getSourcePlatformCode()));

            info.setWarehouseName(warehouseCache.translate(info.getWarehouseCode()));
            //拆分运单号不为空时，两个拆分运单号间用“逗号加空格”分开，方便导出当前页显示拆分运单号
            if(StrUtil.isNotEmpty(info.getSplitWaybillCode())) {
                info.setSplitWaybillCode(info.getSplitWaybillCode().replaceAll(",",", "));
            }
        }).collect(Collectors.toList());
    }

    /**
     * 校验波次查询的商品信息
     *
     * @param goodCodes
     * @param goodsNums
     */
    public void validateGoodsConditions(String[] goodCodes, String[] goodsNums) {
        // 校验条件个数
        if (goodCodes.length != goodsNums.length) {
            throw new ServiceException("商品编码个数与商品数量个数不一致，无法查询");
        }
        // 校验数量是否是数字
        for (String str : goodsNums) {
            if (validateNotBigDecimal(str)) {
                throw new ServiceException("商品数量格式不正确");
            }
        }
    }

    /**
     * 校验字符串是否能不能转为BigDecimal格式；
     *
     * @param str
     * @return
     */
    public boolean validateNotBigDecimal(String str) {
        boolean flag = true;
        try {
            new BigDecimal(str);
            flag = false;
        } catch (Exception e) {

        }
        return flag;
    }

    /**
     * 根据波次商品查询条件，生成子查询sql
     *
     * @param goodCode
     * @param goodsNums
     * @return
     */
    public String makeWaveSubSql(String[] goodCode, String[] goodsNums, Integer containGoods) {
        StringBuilder sql = new StringBuilder();
        /*sql.append(" and a.code in (  SELECT a.delivery_code from wms_delivery_item a where a.delivery_code in ");

        sql.append(" (  select t.delivery_code from wms_delivery_item t where 1=1 ");


        sql.append(" and t.is_deleted = 0 and ( ");
        int count = goodCode.length;
        // 拼接商品信息sql
        for (int i = 0; i < count; i++) {
            sql.append(" ( t.good_code = '").append(goodCode[i]).append("' and t.quantity = ").append(goodsNums[i]).append(" ) or");
        }

        // 去除最后两个字符，即最后一个“or”
        sql = sql.deleteCharAt(sql.length() - 1).deleteCharAt(sql.length() - 1);

        sql.append(" ) ");


        sql.append(" ORDER BY null ) ");
        // 拼接group语句， 判断“是否包含”字段
        if (null == containGoods || NUM_ZERO.equals(containGoods)) {
            sql.append(" GROUP BY a.delivery_code having count(1) = ").append(count);
        } else {
            sql.append(" GROUP BY a.delivery_code having count(1) >= ").append(count);
        }
        sql.append(" ORDER BY null ) ");*/


        int count = goodCode.length;
        for (int i = 0; i < count; i++) {
            sql.append(" and exists (select 1 from wms_delivery_item t where t.delivery_code = a.code and t.good_code = '")
                    .append(goodCode[i]).append("' and t.quantity = ").append(goodsNums[i]).append(" and t.is_deleted = 0) ");
        }

        sql.append(" and (select count(1) from wms_delivery_item b where b.delivery_code = a.code and b.is_deleted = 0) = ").append(count);


        return sql.toString();
    }

    /**
     * 批量拣货完成，更新发货单状态
     *
     * @param wavePickingCode
     * @param status
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void updateStatusByWavePickingCode(String wavePickingCode, String status,String groupCode) {
        //根据波次号更新发货单的状态
        this.update(new UpdateWrapper<Delivery>().lambda().eq(Delivery::getWavePickingCode, wavePickingCode)
                .eq(Delivery::getGroupCode, groupCode)
                .set(Delivery::getStatus, status));
    }


    @Override
    public List<Map<String, Object>> getEnumList(String type) {
        // 1.得到枚举类对象
        Class<DeliverySourceTypeEnum> clz = null;
        try {
            clz = (Class<DeliverySourceTypeEnum>) Class.forName(type);
            // 2.得到所有枚举常量
            Object[] objects = clz.getEnumConstants();
            Method getValue =  clz.getMethod("getValue");
            Method getDesc = clz.getMethod("getDesc");
            List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
            Map<String, Object> map = null;
            for (Object obj : objects) {
                map = new HashMap<String, Object>();
                map.put("code", getValue.invoke(obj));
                map.put("message", getDesc.invoke(obj));
                list.add(map);
            }
            return list;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return new ArrayList<>();
    }

    /**
     * 获取面单号
     *
     * @param code
     */
    /*@Override
    public void getWaybillCode(String code) {
        Delivery delivery = getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, code));
        if (!QIMEN.equals(delivery.getSourceType())) {
            throw new ServiceException("该发货单不是奇门接口订单，不可获取面单号！");
        }
        if (!delivery.getTotalNum().equals(delivery.getSendNum())) {
            throw new ServiceException("该发货单有缺货数量，不可获取面单号！");
        }
        if (ObjectUtil.isNotEmpty(delivery.getWaybillCode())) {
            throw new ServiceException("该发货单已有面单号！");
        }
        if (StrUtil.isEmpty(delivery.getWavePickingCode())) {
            Outbound outbound = iOutboundService.getOne(new QueryWrapper<Outbound>().lambda().eq(Outbound::getDeliveryCode, delivery.getCode()));
            if (ObjectUtil.isNotEmpty(outbound.getStatus()) && !OutboundStatusEnum.FINISH_OUT.getValue().equals(outbound.getStatus())) {
                throw new ServiceException("该发货单对应的出库单尚未出库完成，不可获取面单号！");
            }
        }
        if (StrUtil.isNotEmpty(delivery.getWavePickingCode())) {
            Outbound outbound = iOutboundService.getOne(new QueryWrapper<Outbound>().lambda().eq(Outbound::getWavePickingCode, delivery.getWavePickingCode()));
            if (ObjectUtil.isNotEmpty(outbound.getStatus()) && !OutboundStatusEnum.FINISH_OUT.getValue().equals(outbound.getStatus())) {
                throw new ServiceException("该发货单对应的出库单尚未出库完成，不可获取面单号！");
            }
        }

        PacClient client = new PacClient(dailyAppKey, dailySecretKey, dailyUrl);
        SendSysParams params = new SendSysParams();
        params.setFromCode(dailyToken);
        TmsWaybillGetRequest request = new TmsWaybillGetRequest();
        // 配送公司编码
        request.setCpCode(delivery.getLogisticsCode());
        // 发件人信息
        UserInfoDto sender = new UserInfoDto();
        // 发件人姓名
        sender.setName(delivery.getSenderName());
        // 发件人手机号
        sender.setMobile(delivery.getSenderMobile());

        request.setSender(sender);
        // 寄件地址，这里的地址需要是卖家订购电子面单服务时使用的订购地址，具体可以通过TMS_WAYBILL_SUBSCRIPTION_QUERY接口获取
        AddressDto sendAddress = new AddressDto();
        sendAddress.setProvince(delivery.getSenderProvince());
        sendAddress.setCity(delivery.getSenderCity());
        sendAddress.setDistrict(delivery.getSenderDistrict());
        sendAddress.setDetail(delivery.getSenderDetailAddress());
        sender.setAddress(sendAddress);

        // 获取面单标准模板地址
        CloudprintStandardTemplates cloudprintStandardTemplates = cloudprintStandardTemplatesMapper.selectOne(new QueryWrapper<CloudprintStandardTemplates>().lambda().eq(CloudprintStandardTemplates::getCode, delivery.getLogisticsCode())
                .eq(CloudprintStandardTemplates::getStandardWaybillType, NUM_ONE));
        // 收件信息
        ArrayList<TradeOrderInfoDto> tradeOrderInfoDtos = new ArrayList<TradeOrderInfoDto>();
        request.setTradeOrderInfoDtos(tradeOrderInfoDtos);
        TradeOrderInfoDto tradeOrderInfoDto = new TradeOrderInfoDto();
        tradeOrderInfoDto.setObjectId(delivery.getCode());
        tradeOrderInfoDto.setTemplateUrl(cloudprintStandardTemplates.getStandardTemplateUrl());
        tradeOrderInfoDtos.add(tradeOrderInfoDto);
        OrderInfoDto orderInfoDto = new OrderInfoDto();
        tradeOrderInfoDto.setOrderInfo(orderInfoDto);
        SourcePlatformEnum oct = SourcePlatformEnum.getByValue(delivery.getSourcePlatformCode());
        if(null==oct){
            orderInfoDto.setOrderChannelsType(SourcePlatformEnum.OTHERS.getValue());
        }else{
            orderInfoDto.setOrderChannelsType(delivery.getSourcePlatformCode());
        }
        ArrayList<String> orderList = new ArrayList<String>();
        orderInfoDto.setTradeOrderList(orderList);
        // 订单列表，这里的场景是一个订单获取一个面单号
        orderList.add(delivery.getDeliveryOrderCode());

        PackageInfoDto packageInfoDto = new PackageInfoDto();
        tradeOrderInfoDto.setPackageInfo(packageInfoDto);
        ArrayList<Item> items = new ArrayList<Item>();
        packageInfoDto.setItems(items);
        List<DeliveryItem> deliveryItems = iDeliveryItemService.list(new QueryWrapper<DeliveryItem>().lambda().eq(DeliveryItem::getDeliveryCode, delivery.getCode()));
        deliveryItems.forEach(e1 -> {
            Item item = new Item();
            item.setName(e1.getGoodsName());
            item.setCount(e1.getQuantity().intValue());
            items.add(item);
        });

        UserInfoDto receiver = new UserInfoDto();
        tradeOrderInfoDto.setRecipient(receiver);
        receiver.setName(delivery.getReceiverName());
        receiver.setMobile(delivery.getReceiverMobile());
        AddressDto receiveAddress = new AddressDto();
        receiver.setAddress(receiveAddress);

        receiveAddress.setProvince(delivery.getReceiverProvince());
        receiveAddress.setCity(delivery.getReceiverCity());
        receiveAddress.setDistrict(delivery.getReceiverDistrict());
        receiveAddress.setDetail(delivery.getReceiverDetailAddress());

        TmsWaybillGetResponse response = client.send(request, params);
        if (!response.isSuccess()) {
            if ("S99".equals(response.getErrorCode())) {
                log.error("获取面单超时，进行【1】次重试...");
                response = client.send(request, params);
                if (!response.isSuccess()) {
                    log.error(" errorCode:{},errorMessage:{}", response.getErrorCode(), response.getErrorMsg());
                    if ("S99".equals(response.getErrorCode())) {
                        log.error("获取面单超时，进行【2】次重试...");
                        response = client.send(request, params);
                        if (!response.isSuccess()) {
                            if ("S99".equals(response.getErrorCode())) {
                                log.error("订单【{}】获取面单超时，errorCode:{},errorMessage:{}", code, response.getErrorCode(), response.getErrorMsg());
                                throw new ServiceException(StrUtil.format("订单【{}】获取面单超时，请稍后再试", code));
                            } else {
                                //获取面单失败，更新订单发货状态
                                log.error("订单【{}】获取面单异常，errorCode:{},errorMessage:{}", code, response.getErrorCode(), response.getErrorMsg());
                                //deliveryMapper.updateErrorStatus(deliveryCodes);
                                throw new ServiceException(StrUtil.format("订单【{}】获取面单异常，请稍后再试", code));
                            }
                        }
                    } else {
                        //获取面单失败，更新订单发货状态
                        log.error("订单【{}】获取面单异常，errorCode:{},errorMessage:{}", code, response.getErrorCode(), response.getErrorMsg());
                        //deliveryMapper.updateErrorStatus(deliveryCodes);
                        throw new ServiceException(StrUtil.format("订单【{}】获取面单异常，请稍后再试", code));
                    }
                }
            } else {
                //获取面单失败，更新订单发货状态
                log.error("订单【{}】获取面单异常，errorCode:{},errorMessage:{}", code, response.getErrorCode(), response.getErrorMsg());
                //deliveryMapper.updateErrorStatus(deliveryCodes);
                throw new ServiceException(StrUtil.format("订单【{}】获取面单异常，请稍后再试", code));
            }
        }
        List<WaybillCloudPrintResponse> waybillCloudPrintResponseList = response.getWaybillCloudPrintResponseList();
        log.error("订单【{}】获取面单信息:{}", code, waybillCloudPrintResponseList);
        for (WaybillCloudPrintResponse waybillCloudPrintResponse : waybillCloudPrintResponseList) {
            if (delivery.getCode().equals(waybillCloudPrintResponse.getObjectId())) {
                // 保存订单面单信息
                delivery.setStatus(DeliveryStatusEnum.DELIVERIED.getValue());
                delivery.setWaybillCode(waybillCloudPrintResponse.getWaybillCode());
                JSONObject printData = JSONUtil.parseObj(waybillCloudPrintResponse.getPrintData());
                delivery.setSignature(printData.get("signature").toString());
                delivery.setTemplateUrl(printData.get("templateURL").toString());

                this.updateById(delivery);
                // 发送消息
                deliveryorderConfirm(delivery);
            }
        }

    }*/

    /**
     * 批量获取面单号
     *
     * @param deliveryCodes
     */
    @Override
    public String batchGetWaybillCode(List<String> deliveryCodes) {
        String groupCode = UserUtil.getBranchCode();
        List<Delivery> deliveryList = baseMapper.selectList(new LambdaQueryWrapper<Delivery>()
                .eq(Delivery::getGroupCode,groupCode)
                .select(Delivery::getCode,
                        Delivery::getCarrierType,
                        Delivery::getWaybillCode,
                        Delivery::getLogisticsCode,
                        Delivery::getShipmentCode,
                        Delivery::getCarrierCode,
                        Delivery::getSenderProvince,
                        Delivery::getSenderCity,
                        Delivery::getSenderDistrict,
                        Delivery::getSenderDetailAddress,
                        Delivery::getSenderName,
                        Delivery::getSenderMobile,
                        Delivery::getSourceType,
                        Delivery::getDeliveryOrderCode,
                        Delivery::getSourcePlatformCode,
                        Delivery::getReceiverProvince,
                        Delivery::getReceiverCity,
                        Delivery::getReceiverDistrict,
                        Delivery::getReceiverDetailAddress,
                        Delivery::getReceiverName,
                        Delivery::getReceiverMobile
                )
                .in(Delivery::getCode, deliveryCodes));

        log.info("批量获取面单号【1】，过滤是否有物流承运商");
        List<Delivery> logisticsDeliveryList = deliveryList.stream().filter(d -> StrUtil.equals(d.getCarrierType(), "logistics")).collect(toList());
        if (IterUtil.isNotEmpty(logisticsDeliveryList)) {
            List<String> codes = logisticsDeliveryList.stream().map(Delivery::getCode).collect(toList());
            log.info("批量获取面单号【1】，所选订单包含物流承运商，不能获取面单号。{}", codes);
            throw new ServiceException("所选订单【" + codes + "】承运商为物流，无法获取面单号");
        }
        log.info("批量获取面单号【2】，过滤是否有快递承运商");
        List<Delivery> expressDeliveryList = deliveryList.stream().filter(d -> StrUtil.equals(d.getCarrierType(), "express")).collect(toList());
        if (IterUtil.isEmpty(expressDeliveryList)) {
            log.info("批量获取面单号【2】，所选订单包含没有承运商，不能获取面单号。{}", deliveryCodes);
            throw new ServiceException("所选订单【" + deliveryCodes + "】没有承运商，无法获取面单号");
        }


        //面单号校验，如果面单号有非空的，则提示
        log.info("批量获取面单号【3】，过滤是已有面单号");
        List<Delivery> tempDeliveryList = expressDeliveryList.stream()
                .filter(item -> StrUtil.isNotBlank(item.getWaybillCode())).collect(Collectors.toList());

        if (IterUtil.isNotEmpty(tempDeliveryList)) {

            List<String> codes = tempDeliveryList.stream().map(Delivery::getCode).collect(toList());
            throw new ServiceException("所选订单【" + codes + "】已有面单号，无法获取面单号");
        }
        //获取面单号为空的订单
        deliveryList = expressDeliveryList.stream().filter(item -> StrUtil.isBlank(item.getWaybillCode())).collect(Collectors.toList());
        if (IterUtil.isEmpty(deliveryList)) {
            log.info("批量获取面单号【4】，没有复核获取面单要求的，不能获取面单号。{}", deliveryCodes);
            throw new ServiceException("所选订单【" + deliveryCodes + "】没有复核获取面单要求的，无法获取面单号");
        }


        // 校验是否都是同一承运商
        int logisticsSize = deliveryList.stream().collect(Collectors.groupingBy(Delivery::getCarrierCode)).size();
        if (logisticsSize > NUM_ONE) {
            throw new ServiceException(MUST + CHOOSE + SAME + LOGISTICS_NAME + DELIVERY);
        }
        // 校验是否都是同一货主
        int shipmentSize = deliveryList.stream().collect(Collectors.groupingBy(Delivery::getShipmentCode)).size();
        if (shipmentSize > NUM_ONE) {
            throw new ServiceException(MUST + CHOOSE + SAME + SHIPMENT_NAME + DELIVERY);
        }
        Carrier carriers = carrierCache.getCarrier(deliveryList.get(0).getCarrierCode());
        if (null == carriers) {
            log.error("找不到承运商信息，无法获取面单号。");
            throw new ServiceException("找不到承运商信息");
        }
        switch (carriers.getPlatformType()){
            case "JD":{
                Set<String> sourcePlatformCodeSet = deliveryList.stream().map(Delivery::getSourcePlatformCode).collect(Collectors.toSet());
                if(sourcePlatformCodeSet.size() > NUM_ONE||!sourcePlatformCodeSet.contains("JD")){
                    throw new ServiceException("订单销售平台与承运商平台类型不匹配");
                }
                //TODO 调用京东接口
                break;
            }
            case "PDD":{
                Set<String> sourcePlatformCodeSet = deliveryList.stream()
                        .map(Delivery::getSourcePlatformCode).collect(Collectors.toSet());
                if(sourcePlatformCodeSet.size() > NUM_ONE||!(sourcePlatformCodeSet.contains("PDD")||sourcePlatformCodeSet.contains("PIN_DUO_DUO"))){
                    throw new ServiceException("订单销售平台与承运商平台类型不匹配");
                }
                break;
            } case "DY":{
                Set<String> sourcePlatformCodeSet = deliveryList.stream()
                        .map(Delivery::getSourcePlatformCode).collect(Collectors.toSet());
                if(sourcePlatformCodeSet.size() > NUM_ONE||!(sourcePlatformCodeSet.contains("DY")||sourcePlatformCodeSet.contains("DYXD"))){
                    throw new ServiceException("订单销售平台与承运商平台类型不匹配");
                }
                break;
            } case "TB":{
                Set<String> sourcePlatformCodeSet = deliveryList.stream()
                        .map(Delivery::getSourcePlatformCode).collect(Collectors.toSet());
                if(sourcePlatformCodeSet.size() > NUM_ONE||!(sourcePlatformCodeSet.contains("TB")||sourcePlatformCodeSet.contains("TM"))){
                    throw new ServiceException("订单销售平台与承运商平台类型不匹配");
                }
                break;
            }
            default:{

            }
        }

        //将查出来所有得订单号，按10条分组
        List<List<Delivery>> deliveries = Lists.partition(deliveryList, 10);
        List<Integer> failCodes = Lists.newArrayList();
        // 线程结果集
        Map<Integer, Future<Boolean>> futureMap = Maps.newHashMap();



        //key
        dailyAppKey = carriers.getQimenKey();

        //重新赋值店铺授权token
        String shopCode = carriers.getShopCode();
        if(StrUtil.isNotBlank(shopCode)){
            Shop shop = shopMapper.selectOne(Wrappers.lambdaQuery(Shop.class)
                    .eq(Shop::getGroupCode,groupCode)
                    .select(Shop::getAccessToken,Shop::getRefreshToken)
                    .eq(Shop::getCode,shopCode));
            if(null!=shop&&StrUtil.isNotBlank(shop.getAccessToken())){
                dailyToken = shop.getAccessToken();
            }
        }

        //电子面单密码
        dailySecretKey = carriers.getWaybillPassword();

        //外部编码
        String outerCode = carriers.getOuterCode();

        //模板地址
        String standardTemplateUrl = carriers.getStandardTemplateUrl();

        if (StrUtil.hasBlank(dailyAppKey, dailyToken, dailySecretKey, standardTemplateUrl, outerCode)) {
            log.error("无法获取面单号，承运商缺少必须要信息。{}", carriers);
            throw new ServiceException("无法获取面单号，承运商缺少必须要信息。");
        }

        PacClient client = new PacClient(dailyAppKey, dailySecretKey, dailyUrl);
        for (List<Delivery> deliverys : deliveries) {
            List<String> codes = deliverys.stream().map(Delivery::getCode).collect(toList());
            Future<Boolean> future = batchGetWaybillCodeByDeliverys(deliverys, client, outerCode, standardTemplateUrl);
            if (future == null) {
                failCodes.add(codes.hashCode());
            } else {
                futureMap.put(codes.hashCode(), future);
            }
        }
        StringBuilder failMsg = new StringBuilder();
        futureMap.forEach((key, future) -> {
            try {
                while (true) {
                    if (future.isDone()) {
                        Boolean status = future.get();
                        if (!status) {
                            failCodes.add(key);
                        }
                        break;
                    }
                }
            } catch (ServiceException | InterruptedException | ExecutionException e) {
                failMsg.append("订单:").append(key).append("异常信息:").append(e.getMessage()).append(";");
                failCodes.add(key);
                Thread.currentThread().interrupt();
            }
        });
        return failMsg.toString();

    }

    @Async
    public Future<Boolean> batchGetWaybillCodeByDeliverys(List<Delivery> deliverys, PacClient client, String outCode, String standardTemplateUrl) {

        Delivery delivery = deliverys.get(0);
        // 寄件地址，这里的地址需要是卖家订购电子面单服务时使用的订购地址，具体可以通过TMS_WAYBILL_SUBSCRIPTION_QUERY接口获取
        AddressDto sendAddress = new AddressDto();
        sendAddress.setProvince(delivery.getSenderProvince());
        sendAddress.setCity(delivery.getSenderCity());
        sendAddress.setDistrict(delivery.getSenderDistrict());
        sendAddress.setDetail(delivery.getSenderDetailAddress());

        // 发件人信息
        UserInfoDto sender = new UserInfoDto();
        // 发件人姓名
        sender.setName(delivery.getSenderName());
        // 发件人手机号
        sender.setMobile(delivery.getSenderMobile());
        sender.setAddress(sendAddress);


        TmsWaybillGetRequest request = new TmsWaybillGetRequest();
        // 配送公司编码
        request.setCpCode(outCode);
        request.setSender(sender);
        getWayBill(deliverys, client, request, standardTemplateUrl);
        //boolean flag = true;
        //try{
        //    for (DeliveryVO deliveryVO : deliverys) {
        //        getWayBillCodeByDelivery(deliveryVO);
        //    }
        //} catch (Exception e) {
        //    log.error("获取面单失败，失败信息为："+e.getMessage());
        //    flag = false;
        //}
        return new AsyncResult(true);
    }

    /**
     * 获取面单号
     *
     * @param deliveryVO
     * @return
     * @deprecated use com.arpa.wms.service.impl.DeliveryServiceImpl#getWayBill(java.util.List, com.taobao.pac.sdk.cp.PacClient, com.taobao.pac.sdk.cp.dataobject.request.TMS_WAYBILL_GET.TmsWaybillGetRequest)
     */
    @Deprecated
    private boolean getWayBillCodeByDelivery(Delivery deliveryVO) {
        try {
            // 寄件地址，这里的地址需要是卖家订购电子面单服务时使用的订购地址，具体可以通过TMS_WAYBILL_SUBSCRIPTION_QUERY接口获取
            AddressDto sendAddress = new AddressDto();
            sendAddress.setProvince(deliveryVO.getSenderProvince());
            sendAddress.setCity(deliveryVO.getSenderCity());
            sendAddress.setDistrict(deliveryVO.getSenderDistrict());
            sendAddress.setDetail(deliveryVO.getSenderDetailAddress());


            // 发件人信息
            UserInfoDto sender = new UserInfoDto();
            // 发件人姓名
            sender.setName(deliveryVO.getSenderName());
            // 发件人手机号
            sender.setMobile(deliveryVO.getSenderMobile());
            sender.setAddress(sendAddress);


            TmsWaybillGetRequest request = new TmsWaybillGetRequest();
            // 配送公司编码
            request.setCpCode(deliveryVO.getLogisticsCode());
            request.setSender(sender);

            // 获取面单标准模板地址
            CloudprintStandardTemplates cloudprintStandardTemplates = cloudprintStandardTemplatesMapper.selectOne(new QueryWrapper<CloudprintStandardTemplates>()
                    .lambda().eq(CloudprintStandardTemplates::getCode, deliveryVO.getLogisticsCode())
                    .eq(CloudprintStandardTemplates::getStandardWaybillType, NumConst.NUM_ONE));
            if (null == cloudprintStandardTemplates) {
                log.info("获取面单失败，没有物流模板");
                return false;
            }
            String standardTemplateUrl = cloudprintStandardTemplates.getStandardTemplateUrl();
            ArrayList<TradeOrderInfoDto> orderInfoDtos = new ArrayList<>();

            ArrayList<String> orderList = new ArrayList<>();
            // 订单列表，这里的场景是一个订单获取一个面单号
            orderList.add(deliveryVO.getDeliveryOrderCode());
            OrderInfoDto orderInfoDto = new OrderInfoDto();
            SourcePlatformEnum oct = SourcePlatformEnum.getByValue(deliveryVO.getSourcePlatformCode());
            if (null == oct) {
                orderInfoDto.setOrderChannelsType(SourcePlatformEnum.OTHERS.getValue());
            } else {
                orderInfoDto.setOrderChannelsType(deliveryVO.getSourcePlatformCode());
            }
            orderInfoDto.setTradeOrderList(orderList);
            List<DeliveryItem> deliveryItems = iDeliveryItemService.list(new QueryWrapper<DeliveryItem>().lambda()
                    .eq(DeliveryItem::getGroupCode,UserUtil.getBranchCode())
                    .eq(DeliveryItem::getDeliveryCode, deliveryVO.getCode()).eq(DeliveryItem::getDeleted, NUM_ZERO));
            if (IterUtil.isEmpty(deliveryItems)) {
                log.info("找不到订单明细，订单标识：{}", deliveryVO.getCode());
                return false;
            }
            List<Item> items = deliveryItems.stream().map(e1 -> {
                Item item = new Item();
                item.setName(e1.getGoodsName());
                item.setCount(e1.getQuantity().intValue());
                return item;
            }).collect(Collectors.toList());

            PackageInfoDto packageInfoDto = new PackageInfoDto();
            packageInfoDto.setItems(items);

            AddressDto receiveAddress = new AddressDto();
            receiveAddress.setProvince(deliveryVO.getReceiverProvince());
            receiveAddress.setCity(deliveryVO.getReceiverCity());
            receiveAddress.setDistrict(deliveryVO.getReceiverDistrict());
            receiveAddress.setDetail(deliveryVO.getReceiverDetailAddress());

            UserInfoDto receiver = new UserInfoDto();
            receiver.setName(deliveryVO.getReceiverName());
            receiver.setMobile(deliveryVO.getReceiverMobile());
            receiver.setAddress(receiveAddress);

            TradeOrderInfoDto tradeOrderInfoDto = new TradeOrderInfoDto();
            tradeOrderInfoDto.setObjectId(deliveryVO.getCode());
            tradeOrderInfoDto.setTemplateUrl(standardTemplateUrl);
            tradeOrderInfoDto.setOrderInfo(orderInfoDto);
            tradeOrderInfoDto.setPackageInfo(packageInfoDto);
            tradeOrderInfoDto.setRecipient(receiver);

            orderInfoDtos.add(tradeOrderInfoDto);

            SendSysParams params = new SendSysParams();
            params.setFromCode(dailyToken);

            request.setTradeOrderInfoDtos(orderInfoDtos);
            log.info("调用获取面单接口");
            log.info("交易对象：{}", orderInfoDtos);

            log.info("AppKey：{}", dailyAppKey);
            log.info("SecretKey：{}", dailySecretKey);
            log.info("Url：{}", dailyUrl);
            PacClient client = new PacClient(dailyAppKey, dailySecretKey, dailyUrl);
            TmsWaybillGetResponse response = client.send(request, params);
            if (!response.isSuccess()) {
                if ("S99".equals(response.getErrorCode())) {
                    log.error("获取面单超时，进行【1】次重试...");
                    response = client.send(request, params);
                    if (!response.isSuccess()) {
                        log.error(" errorCode:{},errorMessage:{}", response.getErrorCode(), response.getErrorMsg());
                        if ("S99".equals(response.getErrorCode())) {
                            log.error("获取面单超时，进行【2】次重试...");
                            response = client.send(request, params);
                            if (!response.isSuccess()) {
                                //获取面单失败，更新订单发货状态
                                log.error(GET_WAY_BILL_HAS_EXCEPTION_UPDATE_DELIVERY_STATUS_UN_DELIVERD, deliveryVO.getCode(), response.getErrorCode(), response.getErrorMsg());
                                return false;
                            }
                        } else {
                            //获取面单失败，更新订单发货状态
                            log.error(GET_WAY_BILL_HAS_EXCEPTION_UPDATE_DELIVERY_STATUS_UN_DELIVERD, deliveryVO.getCode(), response.getErrorCode(), response.getErrorMsg());

                            return false;
                        }
                    }
                } else {
                    //获取面单失败，更新订单发货状态
                    log.error(GET_WAY_BILL_HAS_EXCEPTION_UPDATE_DELIVERY_STATUS_UN_DELIVERD, deliveryVO.getCode(), response.getErrorCode(), response.getErrorMsg());

                    return false;
                }
            }
            WaybillCloudPrintResponse waybillCloudPrintResponse = response.getWaybillCloudPrintResponseList().get(NumConst.NUM_ZERO);

            String code = waybillCloudPrintResponse.getObjectId();
            String wayBillCode = waybillCloudPrintResponse.getWaybillCode();
            JSONObject printData = JSONUtil.parseObj(waybillCloudPrintResponse.getPrintData());
            String signature = printData.get("signature").toString();
            String url = printData.get("templateURL").toString();
            deliveryVO.setWaybillCode(wayBillCode);
            deliveryVO.setSignature(signature);
            deliveryVO.setTemplateUrl(url);

            String routeCode = "";

            JSONObject data = JSONUtil.parseObj(printData.get("data"));
            if (null != data) {
                JSONObject routingInfo = JSONUtil.parseObj(data.get("routingInfo"));
                if (null != routingInfo) {
                    if (null != routingInfo.get("routeCode")) {
                        routeCode = routingInfo.get("routeCode").toString();
                    }
                }
            }
            //更新订单物流信息
            this.updateWayBillCode(code, wayBillCode, signature, url, waybillCloudPrintResponse.getPrintData(), routeCode,UserUtil.getBranchCode());
        } catch (Exception e) {
            log.info("获取面单出现异常" + e.getMessage());
        }
        return true;
    }


    /**
     * @param client
     * @param request
     */
    @Transactional(rollbackFor = Exception.class)
    public void getWayBill(List<Delivery> deliveries, PacClient client, TmsWaybillGetRequest request, String standardTemplateUrl) throws ServiceException {
        log.info("Execute method asynchronously - {}", Thread.currentThread().getName());
        if (IterUtil.isEmpty(deliveries)) {
            log.error("找不到符合条件的订单信息");
            throw new ServiceException("找不到符合条件的订单信息");
        }

        // 获取面单标准模板地址
        //CloudprintStandardTemplates cloudprintStandardTemplates = cloudprintStandardTemplatesMapper.selectOne(new QueryWrapper<CloudprintStandardTemplates>()
        //        .lambda().eq(CloudprintStandardTemplates::getCode, carriers.getOuterCode())
        //        .eq(CloudprintStandardTemplates::getStandardWaybillType, NumConst.NUM_ONE));
        //if (null == cloudprintStandardTemplates) {
        //    log.error("获取面单失败，没有物流模板");
        //    throw new ServiceException("获取面单失败，没有物流模板");
        //}
        //String standardTemplateUrl = cloudprintStandardTemplates.getStandardTemplateUrl();
        ArrayList<TradeOrderInfoDto> orderInfoDtos = new ArrayList<>();
        deliveries.forEach(e -> {
            ArrayList<String> orderList = new ArrayList<>();
            if (StrUtil.equals(e.getSourceType(), "qimen")) {
                // 订单列表，这里的场景是一个订单获取一个面单号
                orderList.add(e.getDeliveryOrderCode());
            } else {

                orderList.add(e.getCode());
            }

            OrderInfoDto orderInfoDto = new OrderInfoDto();
            SourcePlatformEnum oct = SourcePlatformEnum.getByValue(e.getSourcePlatformCode());
            if (null == oct) {
                orderInfoDto.setOrderChannelsType(SourcePlatformEnum.OTHERS.getValue());
            } else {
                orderInfoDto.setOrderChannelsType(e.getSourcePlatformCode());
            }
            orderInfoDto.setTradeOrderList(orderList);

            List<DeliveryItem> deliveryItems = this.iDeliveryItemService.list(new LambdaQueryWrapper<DeliveryItem>()
                    .eq(DeliveryItem::getGroupCode,UserUtil.getBranchCode())
                    .eq(DeliveryItem::getDeliveryCode, e.getCode()));
            if (IterUtil.isEmpty(deliveryItems)) {
                log.error("找不到订单明细，订单标识：{}", e.getCode());
                throw new ServiceException("找不到订单明细，订单标识：" + e.getCode());
            }
            List<Item> items = deliveryItems.stream().map(e1 -> {
                Item item = new Item();
                item.setName(e1.getGoodsName());
                item.setCount(e1.getQuantity().intValue());
                return item;
            }).collect(toList());

            PackageInfoDto packageInfoDto = new PackageInfoDto();
            packageInfoDto.setItems(items);

            AddressDto receiveAddress = new AddressDto();
            receiveAddress.setProvince(e.getReceiverProvince());
            receiveAddress.setCity(e.getReceiverCity());
            receiveAddress.setDistrict(e.getReceiverDistrict());
            receiveAddress.setDetail(e.getReceiverDetailAddress());

            UserInfoDto receiver = new UserInfoDto();
            receiver.setName(e.getReceiverName());
            receiver.setMobile(e.getReceiverMobile());
            receiver.setAddress(receiveAddress);

            TradeOrderInfoDto tradeOrderInfoDto = new TradeOrderInfoDto();
            tradeOrderInfoDto.setObjectId(e.getCode());
            tradeOrderInfoDto.setTemplateUrl(standardTemplateUrl);
            tradeOrderInfoDto.setOrderInfo(orderInfoDto);
            tradeOrderInfoDto.setPackageInfo(packageInfoDto);
            tradeOrderInfoDto.setRecipient(receiver);

            orderInfoDtos.add(tradeOrderInfoDto);
        });
        SendSysParams params = new SendSysParams();
        params.setFromCode(dailyToken);

        // 配送公司编码
        request.setTradeOrderInfoDtos(orderInfoDtos);
        log.info("调用获取面单接口：request：{},params:{}", JSONUtil.toJsonStr(request), JSONUtil.toJsonStr(params));
        TmsWaybillGetResponse response = client.send(request, params);
        if (!response.isSuccess()) {
            if ("S99".equals(response.getErrorCode())) {
                log.error("获取面单超时，进行【1】次重试...");
                response = client.send(request, params);
                if (!response.isSuccess()) {
                    log.error(" errorCode:{},errorMessage:{}", response.getErrorCode(), response.getErrorMsg());
                    if ("S99".equals(response.getErrorCode())) {
                        log.error("获取面单超时，进行【2】次重试...");
                        response = client.send(request, params);
                        if (!response.isSuccess()) {
                            //获取面单失败，更新订单发货状态
                            List<String> deliveryCodes = deliveries.stream().map(Delivery::getCode).collect(toList());
                            log.error(GET_WAY_BILL_HAS_EXCEPTION_UPDATE_DELIVERY_STATUS_UN_DELIVERD, deliveryCodes, response.getErrorMsg());

                            throw new ServiceException(StrUtil.format(GET_WAY_BILL_HAS_EXCEPTION_UPDATE_DELIVERY_STATUS_UN_DELIVERD, deliveryCodes, PLEASE_RETRY_AGAIN));
                        }
                    } else {
                        //获取面单失败，更新订单发货状态
                        List<String> deliveryCodes = deliveries.stream().map(Delivery::getCode).collect(toList());
                        log.error(GET_WAY_BILL_HAS_EXCEPTION_UPDATE_DELIVERY_STATUS_UN_DELIVERD, deliveryCodes, response.getErrorMsg());
                        //deliveryMapper.updateErrorStatus(deliveryCodes);
                        throw new ServiceException(StrUtil.format(GET_WAY_BILL_HAS_EXCEPTION_UPDATE_DELIVERY_STATUS_UN_DELIVERD, deliveryCodes, PLEASE_RETRY_AGAIN));
                    }
                }
            } else {
                //获取面单失败，更新订单发货状态
                List<String> deliveryCodes = deliveries.stream().map(Delivery::getCode).collect(toList());
                log.error(GET_WAY_BILL_HAS_EXCEPTION_UPDATE_DELIVERY_STATUS_UN_DELIVERD, deliveryCodes, response.getErrorMsg());
                //deliveryMapper.updateErrorStatus(deliveryCodes);
                throw new ServiceException(StrUtil.format(GET_WAY_BILL_HAS_EXCEPTION_UPDATE_DELIVERY_STATUS_UN_DELIVERD, deliveryCodes,response.getErrorMsg()));
            }
        }
        List<WaybillCloudPrintResponse> waybillCloudPrintResponseList = response.getWaybillCloudPrintResponseList();
        for (WaybillCloudPrintResponse waybillCloudPrintResponse : waybillCloudPrintResponseList) {
            String code = waybillCloudPrintResponse.getObjectId();
            String wayBillCode = waybillCloudPrintResponse.getWaybillCode();
            JSONObject printData = JSONUtil.parseObj(waybillCloudPrintResponse.getPrintData());
            String signature = printData.get("signature").toString();
            String url = printData.get("templateURL").toString();
            log.info("更新订单【{}】状态,面单号【{}】", code, wayBillCode);
            String routeCode = "";

            JSONObject data = JSONUtil.parseObj(printData.get("data"));
            if (null != data) {
                JSONObject routingInfo = JSONUtil.parseObj(data.get("routingInfo"));
                if (null != routingInfo) {
                    if (null != routingInfo.get("routeCode")) {
                        routeCode = routingInfo.get("routeCode").toString();
                    }
                }
            }
            //更新订单物流信息
            this.updateWayBillCode(code, wayBillCode, signature, url, waybillCloudPrintResponse.getPrintData(), routeCode,UserUtil.getBranchCode());

        }
        List<String> success = waybillCloudPrintResponseList.stream().map(WaybillCloudPrintResponse::getObjectId).collect(toList());
        List<String> deliveryCodes = deliveries.stream().map(Delivery::getCode).collect(toList());

        // 如果没获取到面单号，则将状态更新为 待发货
        List<String> reduce = success.stream().filter(item -> !deliveryCodes.contains(item)).collect(toList());
        if (IterUtil.isNotEmpty(reduce)) {
            log.info("未获取到面单号,订单标识{}", reduce);
            throw new ServiceException("未获取到面单号,订单标识：" + reduce);
        }
    }



    /**
     * 根据波次号，更新发货单拣货数量
     *
     * @param wavePickingCode
     * @return
     */
    @Override
    public int updatePickingQuantityByWavePickingCode(String wavePickingCode,String groupCode) {
        return this.baseMapper.updatePickingQuantityByWavePickingCode(wavePickingCode,groupCode);
    }

    /**
     * 出库确认获取波次内所有订单的状态分组，用来判断出库单状态
     *
     * @param wavePickingCode 波次号
     * @return java.util.List<java.lang.String>
     * @author liuxr
     * @date 2021/4/23 17:36
     */
    @Override
    public Set<String> findDeliveryStatusForConfirmDelivery(String wavePickingCode,String groupCode) {
        return baseMapper.findDeliveryStatusForConfirmDelivery(wavePickingCode,groupCode);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Delivery> findDeliveryForConfirmDelivery(String wavePickingCode,String groupCode) {
        return baseMapper.findDeliveryForConfirmDelivery(wavePickingCode,groupCode);
    }


    /**
     * 更新发货单状态
     *
     * @param wavePickingCode
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStatusByWavePickingCode(String wavePickingCode, Integer type) {
        if (NUM_ZERO.equals(type)) {
            // 订单来源不是qimen的，状态更新为发货完成
            this.update(new UpdateWrapper<Delivery>().lambda().eq(Delivery::getWavePickingCode, wavePickingCode).ne(Delivery::getSourceType, CommonConst.QIMEN)
                    .set(Delivery::getStatus, DeliveryStatusEnum.DELIVERIED.getValue()));

            // 订单来源是qimen的，并且总数量等于发货数量，状态更新为发货中
            this.update(new UpdateWrapper<Delivery>().lambda().eq(Delivery::getWavePickingCode, wavePickingCode).eq(Delivery::getSourceType, CommonConst.QIMEN)
                    .apply(" total_num = send_num").set(Delivery::getStatus, DeliveryStatusEnum.DELIVERYING.getValue()));

            // 订单来源是qimen的，总数量不等于发货数量，状态更新为待发货
            this.update(new UpdateWrapper<Delivery>().lambda().eq(Delivery::getWavePickingCode, wavePickingCode).eq(Delivery::getSourceType, CommonConst.QIMEN)
                    .apply(" total_num <> send_num").set(Delivery::getStatus, DeliveryStatusEnum.UN_DELIVERY.getValue()));
        }

        if (NUM_ONE.equals(type)) {
            // 订单来源是不是qimen的，状态都更新为待发货
            this.update(new UpdateWrapper<Delivery>().lambda().eq(Delivery::getWavePickingCode, wavePickingCode).set(Delivery::getStatus, DeliveryStatusEnum.UN_DELIVERY.getValue()));
        }

    }

    /**
     * 发货单确认消息, 用于订单更新奇门单据
     *
     * @param delivery
     */
    @Override
    public boolean deliveryorderConfirmForOutCheck(Delivery delivery) throws ApiException {
        log.info("订单出库完成发送确认信息：{}", delivery.getCode());
        DeliveryorderConfirmRequest req = new DeliveryorderConfirmRequest();
        req.setCustomerId(delivery.getCustomerId());
        req.setVersion("2.0");
        DeliveryorderConfirmRequest.DeliveryOrder deliveryOrder = new DeliveryorderConfirmRequest.DeliveryOrder();
        // 出库单号
        deliveryOrder.setDeliveryOrderCode(delivery.getDeliveryOrderCode());
        // 仓库编码
        deliveryOrder.setWarehouseCode(delivery.getWarehouseCode());
        // 出库单类型
        deliveryOrder.setOrderType(delivery.getOrderType());

        // 商品列表
        List<DeliveryorderConfirmRequest.Item> items = new ArrayList<>();
        List<DeliveryItem> deliveryItemList = iDeliveryItemService.list(new QueryWrapper<DeliveryItem>().lambda().eq(DeliveryItem::getGroupCode,UserUtil.getBranchCode()).eq(DeliveryItem::getDeliveryCode, delivery.getCode()));
        deliveryItemList.forEach(e -> {
            DeliveryorderConfirmRequest.Item item = new DeliveryorderConfirmRequest.Item();
            // 商品编码
            item.setItemCode(e.getGoodCode());
            // 包裹内该商品数量
            item.setQuantity(e.getOutboundQuantity().longValue());
            items.add(item);
        });

        // 包裹信息
        List<DeliveryorderConfirmRequest.Package> packages = new ArrayList<>();
        DeliveryorderConfirmRequest.Package package1 = new DeliveryorderConfirmRequest.Package();
        // 物流公司编码
        package1.setLogisticsCode(delivery.getLogisticsCode());
        // 运单号
        package1.setExpressCode(delivery.getWaybillCode());
        package1.setItems(items);
        packages.add(package1);

        req.setPackages(packages);
        deliveryOrder.setItems(items);

        // 单据列表
        List<DeliveryorderConfirmRequest.OrderLine> orderLines = new ArrayList<>();
        deliveryItemList.stream().forEach(e -> {
            DeliveryorderConfirmRequest.OrderLine orderLine = new DeliveryorderConfirmRequest.OrderLine();
            // 商品编码
            orderLine.setItemCode(e.getGoodCode());
            // 应发商品数量
            orderLine.setPlanQty((e.getPlanQuantity().intValue()) + "");
            // 实发商品数量
            orderLine.setActualQty((e.getOutboundQuantity().intValue()) + "");
            orderLine.setActualPrice(e.getActualPrice().toString());

            orderLines.add(orderLine);
        });

        req.setDeliveryOrder(deliveryOrder);
        req.setOrderLines(orderLines);
        QimenClient client = new DefaultQimenClient(url, appkey, secret);
        DeliveryorderConfirmResponse rsp = client.execute(req);
        if (StrUtil.equals(rsp.getFlag(), "success")) {
            return true;
        } else {
            log.error("奇门订单更新失败：{}", rsp.getMessage());
            return false;
        }
    }

    /**
     * 根据订单的分配单减少临时库存
     *
     * @param deliveryCode
     */
    @Override
    public void releaseTempInv(String deliveryCode) {
        this.baseMapper.releaseTempInv(deliveryCode,UserUtil.getBranchCode());
    }

    /**
     * 更新订单面单信息
     *
     * @param code        发货单标识
     * @param wayBillCode 面单号
     * @param signature   验签
     * @param url         模板url
     */
    @Override
    public void updateWayBillCode(String code, String wayBillCode, String signature, String url, String printData, String routeCode,String groupCode) {
        this.baseMapper.updateWayBillCode(code, wayBillCode, signature, url, printData, routeCode, groupCode);
    }

    /**
     * 更新订单备注
     *
     * @param deliveryCodes 订单号集合
     * @param remarks
     */
    @Override
    public void updateRemarks(List<String> deliveryCodes, String remarks,String groupCode) {
        this.baseMapper.updateRemarks(deliveryCodes, remarks, groupCode);
    }

    /**
     * 根据订单号统计承运商数量
     *
     * @param codes 订单号
     * @return
     */
    @Override
    public int countLogistics(List<String> codes) {
        return this.baseMapper.countLogistics(codes,UserUtil.getBranchCode());
    }

    /**
     * 根据订单号统计货主数量
     *
     * @param codes 订单号
     * @return
     */
    @Override
    public int countShipment(List<String> codes) {
        return this.baseMapper.countShipment(codes,UserUtil.getBranchCode());
    }

    /**
     * 根据订单号统计波次数量
     *
     * @param codes            订单号
     * @param maxOrderQuantity 单波次最大订单数
     * @return
     */
    @Override
    public int countWave(List<String> codes, Integer maxOrderQuantity) {
        List<Map<String, Object>> groupInfo = this.baseMapper.queryWaveGroupInfo(codes,UserUtil.getBranchCode());
        if (IterUtil.isEmpty(groupInfo)) {
            return 0;
        }
        List<Map<String, Object>> mapList;
        if (null != maxOrderQuantity && maxOrderQuantity > 0) {
            int waveCount = 0;
            List<Map<String, Object>> temp = groupInfo.stream()
                    .filter(g -> maxOrderQuantity >= MapUtil.getLong(g, COUNT))
                    .collect(Collectors.toList());
            if (IterUtil.isNotEmpty(temp)) {
                waveCount = temp.size();
            }
            //订单数量大于最大订单波次数量
            List<Map<String, Object>> map = groupInfo.stream()
                    .filter(g -> maxOrderQuantity < MapUtil.getLong(g, COUNT)).collect(Collectors.toList());
            if (IterUtil.isNotEmpty(map)) {
                for (Map<String, Object> m : map) {
                    //总记录数
                    int count = MapUtil.getInt(m, COUNT);
                    //总页数
                    int totalpage = (count + (maxOrderQuantity - 1)) / maxOrderQuantity;
                    //TODO 后期改成多线程
                    waveCount += totalpage;
                }
            }
            return waveCount;
        } else {
            return groupInfo.size();
        }
    }


    /**
     * 生成波次
     *
     * @param codes            订单号
     * @param maxOrderQuantity 单波次最大订单数
     * @param temporaryCode    临时波次标识
     * @param isReview         是否复核：0不复核，1复核
     * @return
     */
    @Override
    public void genWave(List<String> codes, Integer maxOrderQuantity, String temporaryCode, int isReview) {
        String groupCode = UserUtil.getBranchCode();
        log.info("生成波次，订单号:{}，单波次最大订单数:{}，临时波次标识：{}，是否复核（0不复核，1复核）：{}", codes,maxOrderQuantity,temporaryCode,isReview);
        //获取货主、承运商分组信息，通常是一组一个波次
        List<Map<String, Object>> waveGroupInfoMap = this.baseMapper.queryWaveGroupInfo(codes,groupCode);
        if (IterUtil.isEmpty(waveGroupInfoMap)) {
            log.error("生成波次找不到符合条件的订单信息，订单号:{}", codes);
            throw new ServiceException("生成波次找不到符合条件的订单信息，订单号:" + codes);
        }
        log.info("生成波次，订单号:{}，按货主、承运商分组订单信息：{}", codes,JSONUtil.toJsonStr(waveGroupInfoMap));
        List<Map<String, Object>> mapList;
        List<Integer> failCodes = Lists.newArrayList();
        Map<Integer, Future<Boolean>> futureMap = Maps.newHashMap();
        Map<Integer, Map<String, Object>> allMap = Maps.newHashMap();
        StringBuilder failMsg = new StringBuilder();

        if (null != maxOrderQuantity && maxOrderQuantity > 0) {
            List<Map<String, Object>> temp = waveGroupInfoMap.stream()
                    .filter(g -> maxOrderQuantity >= MapUtil.getLong(g, COUNT))
                    .collect(Collectors.toList());
            mapList = getMaps(temporaryCode, temp);
            //订单数量大于最大订单波次数量
            List<Map<String, Object>> map = waveGroupInfoMap.stream()
                    .filter(g -> maxOrderQuantity < MapUtil.getLong(g, COUNT)).collect(Collectors.toList());
            if (IterUtil.isNotEmpty(map)) {
                for (Map<String, Object> m : map) {
                    //总记录数
                    long count = MapUtil.getLong(m, COUNT);
                    //总页数
                    long totalpage = (count + (maxOrderQuantity - 1)) / maxOrderQuantity;
                    //TODO 后期改成多线程
                    for (int i = 0; i < totalpage; i++) {
                        Map<String, Object> tmp = new HashMap<>();
                        tmp.put(SHIPMENT_CODE, MapUtil.getStr(m, SHIPMENT_CODE));
                        tmp.put(LOGISTICS_CODE, MapUtil.getStr(m, LOGISTICS_CODE));
                        tmp.put(PREPACKAGEDCODE, MapUtil.getInt(m, PREPACKAGEDCODE));
                        tmp.put(TEMPORARY_CODE, temporaryCode);
                        tmp.put(PAGE_NUM, i);
                        tmp.put(PAGE_SIZE, maxOrderQuantity);
                        mapList.add(tmp);
                    }
                }
            }
        } else {
            mapList = getMaps(temporaryCode, waveGroupInfoMap);
        }
        log.info("生成波次，临时波次标识:{}，波次生成元数据:{}", temporaryCode, mapList);
        if (IterUtil.isNotEmpty(mapList)) {
            for (Map<String, Object> m : mapList) {
                allMap.put(m.hashCode(), m);
                //入驻商
                String shipmentCode = MapUtil.getStr(m, SHIPMENT_CODE);
                //承运商
                String logisticsCode = MapUtil.getStr(m, LOGISTICS_CODE);
                //是否预打包订单 0-否 1-是
                String prepackagedCode = MapUtil.getStr(m, PREPACKAGEDCODE);
                Integer pageNum = (Integer) m.get(PAGE_NUM);
                Integer pageSize = (Integer) m.get(PAGE_SIZE);
                Future<Boolean> future = runAsyncGenWave(shipmentCode, logisticsCode, temporaryCode,prepackagedCode, isReview, pageNum, pageSize,UserUtil.getBranchCode());
                if (future == null) {
                    failCodes.add(m.hashCode());
                } else {
                    futureMap.put(m.hashCode(), future);
                }
            }
            futureMap.forEach((key, future) -> {
                try {
                    while (true) {
                        if (future.isDone()) {
                            Boolean status = future.get();
                            if (!status) {
                                failCodes.add(key);
                            }
                            break;
                        }
                    }
                } catch (ServiceException | InterruptedException | ExecutionException e) {
                    log.error("生成波次【{}】线程获取结果【{}】", key, e.getMessage());
                    failMsg.append("生成波次:").append(key).append("异常信息:").append(e.getMessage()).append(";");
                    failCodes.add(key);
                    Thread.currentThread().interrupt();
                }
            });
            if (IterUtil.isNotEmpty(failCodes)) {
                throw new ServiceException(failMsg.toString());
            }
        } else {
            log.error("没有找到要生成波次的订单数据，临时波次标识：{}；", temporaryCode);
            throw new ServiceException("没有要生成波次的订单数据");
        }
    }


    /**
     * 更新生成波次状态
     *
     * @param codes
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String updateWaveStatus(List<String> codes) {
        String uuid = IdUtil.simpleUUID();
        baseMapper.updateWaveStatus(codes,uuid,UserUtil.getBranchCode());
        return uuid;
    }

    /**
     * 多线程异步创建波次
     *
     * @param shipmentCode
     * @param logisticsCode
     * @param temporaryCode
     * @param isReview      是否复核：0不复核，1复核
     * @param pageNum
     * @param pageSize
     * @return
     */
    @Async
    @Transactional(rollbackFor = Exception.class)
    public Future<Boolean> runAsyncGenWave(
            String shipmentCode,
            String logisticsCode,
            String temporaryCode,
            String prepackagedCode,
            int isReview,
            Integer pageNum,
            Integer pageSize,
            String groupCode) {
        runGenWave(shipmentCode, logisticsCode, temporaryCode,prepackagedCode, isReview, pageNum, pageSize, groupCode);
        return new AsyncResult<>(true);
    }


    /**
     * 生成波次数据
     * @author liuxr
     * @date 2021/11/24 15:19
     * @param shipmentCode 货主标识
     * @param logisticsCode 承运商标识
     * @param temporaryCode 临时波次
     * @param prepackagedCode
     * @param isReview
     * @param pageNum
     * @param pageSize
     */
    public void runGenWave(
            String shipmentCode,
            String logisticsCode,
            String temporaryCode,
            String prepackagedCode,
            int isReview,
            Integer pageNum,
            Integer pageSize,
            String groupCode) throws ServiceException {

        Delivery delivery = this.baseMapper.getFirstDeliveryInfo(shipmentCode, logisticsCode, temporaryCode,prepackagedCode);
        if (delivery == null) {
            log.error("找不到订单信息，入驻商:{},承运商:{},临时波次标识:{}", shipmentCode, logisticsCode, temporaryCode);
            throw new ServiceException("找不到订单信息，入驻商:" + shipmentCode + ",承运商:" + logisticsCode + ",临时波次标识:" + temporaryCode);
        }
        log.info("生成波次，入驻商:{},承运商:{},临时波次标识:{}，获取发货单信息：{}", shipmentCode, logisticsCode, temporaryCode, JSONUtil.toJsonStr(delivery));
        BigDecimal distributionNumSum = this.baseMapper.getDistributionNumSum(shipmentCode, logisticsCode, temporaryCode, pageNum, pageSize,prepackagedCode);
        if (distributionNumSum.compareTo(BigDecimal.ZERO) == 0) {
            log.error("波次创建失败，订单分配数量为0，入驻商:{},承运商:{},临时波次标识:{},起始页:{},偏移量:{}", shipmentCode, logisticsCode, temporaryCode, pageNum, pageSize);
            throw new ServiceException("波次创建失败，订单分配数量为0，入驻商:" + shipmentCode + ",承运商:" + logisticsCode + ",临时波次标识:" + temporaryCode + ",起始页:" + pageNum + ",偏移量:" + pageSize);
        }
        log.info("生成波次，统计发货单分配数量总和:{}", distributionNumSum);

        List<Map<String, Object>> distributionMap = this.baseMapper.queryDeliveryDistributionWaveGroupInfo(shipmentCode,groupCode, logisticsCode, temporaryCode, pageNum, pageSize,prepackagedCode);
        if (IterUtil.isEmpty(distributionMap)) {
            log.error("找不到订单分配信息，入驻商:{},承运商:{},临时波次标识:{}", shipmentCode, logisticsCode, temporaryCode);
            throw new ServiceException("找不到订单分配信息，入驻商:" + shipmentCode + ",承运商:" + logisticsCode + ",临时波次标识:" + temporaryCode);
        }
        log.info("生成波次，入驻商:{},承运商:{},临时波次标识:{}，获取发货单分配明细按库存分组:{}", shipmentCode, logisticsCode, temporaryCode, distributionMap);
        //生成波次拣货单号
        String wavePickingCode = OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.BC, shipmentCode,groupCode);

        log.info("生成波次，入驻商:{},承运商:{},临时波次标识:{}，生成波次号:{}", shipmentCode, logisticsCode, temporaryCode, wavePickingCode);

        /******生成拣货单信息*******/
        //生成拣货单号
        String pickingCode = OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.PK, shipmentCode,groupCode);
        log.info("生成波次，入驻商:{},承运商:{},临时波次标识:{}，生成拣货单号:{}", shipmentCode, logisticsCode, temporaryCode, pickingCode);
        List<String> deliveryCodeList = baseMapper.getDeliveryCodesByShipmentCodeAndLogisticsCodeAndTemporaryCode(shipmentCode, logisticsCode, temporaryCode,prepackagedCode,groupCode);

        log.info("生成波次，入驻商:{},承运商:{},临时波次标识:{}，获取所有要生成波次的订单号:{}", shipmentCode, logisticsCode, temporaryCode, JSONUtil.toJsonStr(deliveryCodeList));


        int  goodsSortQuantity = iDeliveryItemService.count(Wrappers.lambdaQuery(DeliveryItem.class)
                .in(DeliveryItem::getDeliveryCode, deliveryCodeList)
                .groupBy(DeliveryItem::getGoodCode));

        log.info("生成波次，入驻商:{},承运商:{},临时波次标识:{}，获取所有明细商品种数:{}", shipmentCode, logisticsCode, temporaryCode,goodsSortQuantity);

        Picking picking = new Picking();
        BeanUtil.copyProperties(delivery, picking);
        picking.setReview(isReview);
        picking.setCode(pickingCode);
        picking.setStockoutQuantity(BigDecimal.ZERO);
        picking.setNotPickingQuantity(distributionNumSum);
        picking.setWavePickingCode(wavePickingCode);
        picking.setPlanQuantity(distributionNumSum);
        picking.setStatus(PickingStatusEnum.NOT_PICK.getValue());
        picking.setTemporaryCode(temporaryCode);
        picking.setCreatedBy(UserUtil.getCode());
        picking.setCreatedName(partyCache.translate(UserUtil.getCode()));
        picking.setGmtCreated(LocalDateTime.now());
        picking.setModifiedBy(UserUtil.getCode());
        picking.setModifiedName(partyCache.translate(UserUtil.getCode()));
        picking.setGmtModified(LocalDateTime.now());
        picking.setPrepackagedCode(prepackagedCode);
        if (StrUtil.isNotEmpty(prepackagedCode)) {
            picking.setPrepackaged(NUM_ONE);
        } else {
            picking.setPrepackaged(NUM_ZERO);
        }
        picking.setGoodsSortQuantity(BigDecimal.valueOf(goodsSortQuantity));




        /******生成拣货单明细，拣货登记明细*******/
        //获取发货单分配明细按库存分组

        List<PickingItem> pickingItemList = new ArrayList<>();
        List<PickingRegister> pickingRegisterList = new ArrayList<>();
        for (Map<String, Object> d : distributionMap) {
            //库存标识
            String inventoryCode = MapUtil.getStr(d, "inventoryCode");
            String distributionQuantitySum = MapUtil.getStr(d, "distributionQuantitySum");

            List<String> deliveryCodes = baseMapper.getDeliveryCodesByShipmentCodeAndLogisticsCodeAndTemporaryCode(shipmentCode, logisticsCode, temporaryCode,prepackagedCode,groupCode);
            DeliveryDistribution deliveryDistribution = this.baseMapper.getFirstDistributionInfo(inventoryCode, deliveryCodes);

            if (null == deliveryDistribution) {
                log.error("找不到订单库存分配信息，入驻商:{},承运商:{},临时波次标识:{},库存标识:{}", shipmentCode, logisticsCode, temporaryCode, inventoryCode);
                throw new ServiceException("找不到订单库存分配信息，入驻商:" + shipmentCode + ",承运商:" + logisticsCode + ",临时波次标识:" + temporaryCode + ",库存标识:" + inventoryCode);
            }
            log.info("生成波次，入驻商:{},承运商:{},临时波次标识:{}，获取订单库存分配信息:{}", shipmentCode, logisticsCode, temporaryCode, deliveryDistribution.toString());
            PickingItem pickingItem = new PickingItem();

            pickingItem.setTemporaryCode(temporaryCode);
            PickingRegister pickingRegister = new PickingRegister();
            BeanUtil.copyProperties(deliveryDistribution, pickingItem);
            pickingItem.setCode(IdUtil.simpleUUID());
            pickingItem.setPickingCode(picking.getCode());
            pickingItem.setDeliveryItemCode(StrUtil.EMPTY);
            // 使用商品基本单位
            if (StrUtil.isNotEmpty(deliveryDistribution.getBasicUnit())) {
                pickingItem.setGoodsUnit(deliveryDistribution.getBasicUnit());
            }
            if (StrUtil.isNotEmpty(deliveryDistribution.getBasicUnitName())) {
                pickingItem.setGoodsUnitName(deliveryDistribution.getBasicUnitName());
            }
            pickingItem.setStockoutQuantity(BigDecimal.ZERO);
            pickingItem.setDistributionQuantity(new BigDecimal(distributionQuantitySum));
            pickingItem.setPlanQuantity(new BigDecimal(distributionQuantitySum));
            // 待拣货状态
            pickingItem.setStatus(PickingStatusEnum.NOT_PICK.getValue());
            pickingItem.setWavePickingCode(wavePickingCode);
            //商品信息

            pickingItem.setGoodCode(deliveryDistribution.getGoodCode());
            pickingItem.setGoodsName(deliveryDistribution.getGoodName());
            pickingItem.setGoodsBarCode(deliveryDistribution.getBarCode());

            pickingItem.setCreatedBy(UserUtil.getCode());
            pickingItem.setCreatedName(partyCache.translate(UserUtil.getCode()));
            pickingItem.setGmtCreated(LocalDateTime.now());

            pickingItem.setDeliveryDistributionCode(deliveryDistribution.getCode());
            BeanUtil.copyProperties(pickingItem, pickingRegister);
            pickingRegister.setCode(IdUtil.simpleUUID());
            pickingRegister.setPickingItemCode(pickingItem.getCode());
            pickingRegister.setNotPickingQuantity(pickingRegister.getDistributionQuantity());
            pickingRegister.setBasicQuantity(BigDecimal.ONE);
            pickingRegister.setTemporaryCode(temporaryCode);
            pickingItemList.add(pickingItem);
            pickingRegisterList.add(pickingRegister);
        }

        updateDeliverysInfo(shipmentCode,logisticsCode,wavePickingCode,temporaryCode,pageNum,pageSize,prepackagedCode);
        //由于用到订单的波次号，所以需要先更新订单信息，然后生成商品订单标识
        String orderTag = generateOrderTag(shipmentCode, logisticsCode, wavePickingCode,prepackagedCode);
        picking.setOrderTag(orderTag);
        List<Picking> pickingList = new ArrayList<>();
        pickingList.add(picking);
        //批量保存拣货单，拣货明细，拣货登记信息
        iPickingService.savePickingsInfo(pickingList,pickingItemList, pickingRegisterList);

    }

    @Transactional(rollbackFor = {Exception.class})
    public void updateDeliverysInfo (String shipmentCode,String logisticsCode,String wavePickingCode,String temporaryCode,Integer pageNum,Integer pageSize,String prepackagedCode) {
        //更新订单状态
        log.info("生成波次，更新订单状态,波次号：{},入驻商：{},承运商：{},临时标识：{},起始页：{},条数：{}", wavePickingCode, shipmentCode, logisticsCode, temporaryCode, pageNum, pageSize);
//        this.baseMapper.updateDeliveryStatus(shipmentCode, logisticsCode, wavePickingCode, temporaryCode, pageNum, pageSize,prepackagedCode);
        //先查询ID列表后更新
        List<Long> deliveryStatusIdList = this.baseMapper.getDeliveryStatusIdList(shipmentCode, logisticsCode, wavePickingCode, temporaryCode, pageNum, pageSize, prepackagedCode);

        if (CollectionUtils.isNotEmpty(deliveryStatusIdList)){
            this.baseMapper.updateDeliveryStatus2(wavePickingCode,deliveryStatusIdList,UserUtil.getBranchCode());
        }

        //先查询所有的code然后再更新
        log.info("生成波次，更新订单明细波次号，入驻商:{},承运商:{},临时波次标识:{}", shipmentCode, logisticsCode, temporaryCode);

        List<String> delveryCodes = this.baseMapper.getDeliveryCodes(wavePickingCode,UserUtil.getBranchCode());
        this.baseMapper.updateDeliveryItem(wavePickingCode, delveryCodes,UserUtil.getBranchCode());
        log.info("生成波次，更新订单分配明细波次号，入驻商:{},承运商:{},临时波次标识:{}", shipmentCode, logisticsCode, temporaryCode);
        this.baseMapper.updateDeliveryDistribution(wavePickingCode, delveryCodes,UserUtil.getBranchCode());
    }

    /**
     * 生成订单标识
     *
     * @return
     */
    private String generateOrderTag(String shipmentCode, String logisticsCode, String wavePickingCode,String prepackagedCode) {
        String orderTag = "";
        //查询总的订单
        List<Delivery> deliveryList = this.list(new QueryWrapper<Delivery>().select("code","goods_sort_quantity","distribution_num").lambda().eq(Delivery::getShipmentCode, shipmentCode)
                .isNull(ObjectUtil.isNull(logisticsCode), Delivery::getCarrierCode)
                .eq(ObjectUtil.isNotNull(logisticsCode), Delivery::getCarrierCode, logisticsCode)
                .eq(Delivery::getWavePickingCode, wavePickingCode)
                .eq(Delivery::getDeleted, NUM_ZERO)
                .isNull(ObjectUtil.isNull(prepackagedCode), Delivery::getPrepackagedCode)
                .eq(ObjectUtil.isNotNull(prepackagedCode), Delivery::getPrepackagedCode, prepackagedCode));
        //查询单品订单
        List<Delivery> singleDeliveryList = this.list(new QueryWrapper<Delivery>().select("distribution_num").lambda().eq(Delivery::getShipmentCode, shipmentCode)
                .isNull(ObjectUtil.isNull(logisticsCode), Delivery::getCarrierCode)
                .eq(ObjectUtil.isNotNull(logisticsCode), Delivery::getCarrierCode, logisticsCode)
                .eq(Delivery::getWavePickingCode, wavePickingCode).eq(Delivery::getOrderTag, "单品")
                .eq(Delivery::getDeleted, NUM_ZERO)
                .isNull(ObjectUtil.isNull(prepackagedCode), Delivery::getPrepackagedCode)
                .eq(ObjectUtil.isNotNull(prepackagedCode), Delivery::getPrepackagedCode, prepackagedCode));
        //单品商品数量大于1的集合
        List<Delivery> singleDeliveryNotOneList = singleDeliveryList.stream().filter(item -> item.getDistributionNum().compareTo(BigDecimal.ONE) > NUM_ZERO).collect(Collectors.toList());
        //查询多品订单
        List<Delivery> multiDeliveryList = this.list(new QueryWrapper<Delivery>().lambda().eq(Delivery::getShipmentCode, shipmentCode)
                .isNull(ObjectUtil.isNull(logisticsCode), Delivery::getCarrierCode)
                .eq(ObjectUtil.isNotNull(logisticsCode), Delivery::getCarrierCode, logisticsCode)
                .eq(Delivery::getWavePickingCode, wavePickingCode)
                .isNull(ObjectUtil.isNull(prepackagedCode), Delivery::getPrepackagedCode)
                .eq(ObjectUtil.isNotNull(prepackagedCode), Delivery::getPrepackagedCode, prepackagedCode)
                .eq(Delivery::getOrderTag, "多品")
                .eq(Delivery::getDeleted, NUM_ZERO));
        //订单商品种数分组
        List<Map<String, Object>> mapList = getDeliveryInfoByGroup(deliveryList);
        //分组后数据只有一条，则为爆品
        if (mapList.size() == NumConst.NUM_ONE) {
            //爆品
            orderTag = "4";
        }
        if (mapList.size() > NumConst.NUM_ONE) {
            //单品业务
            if (IterUtil.isEmpty(multiDeliveryList)) {
                if (IterUtil.isEmpty(singleDeliveryNotOneList)) {
                    orderTag = "1";
                } else {
                    //单品多件
                    orderTag = "2";
                }
            } else {
                //订单商品种数分组
                int goodsSortQuantitySize = deliveryList.stream().collect(Collectors.groupingBy(item -> item.getGoodsSortQuantity())).size();
                //订单分配数量分组
                int distributionSize = deliveryList.stream().collect(Collectors.groupingBy(item -> item.getDistributionNum())).size();
                List<String> deliveryCodes = deliveryList.stream().map(e -> e.getCode()).collect(Collectors.toList());
                mapList = iDeliveryItemService.getDeliveryItemList(deliveryCodes);
                //爆品条件：1.订单的商品种类和数量都对应一致
                if (goodsSortQuantitySize == NumConst.NUM_ONE && distributionSize == NumConst.NUM_ONE && mapList.size() ==
                        multiDeliveryList.get(NUM_ZERO).getGoodsSortQuantity().intValue()) {
                    //爆品
                    orderTag = "4";
                } else {
                    //多品
                    orderTag = "3";
                }
            }
        }
        return orderTag;
    }

    /**
     * 根据订单明细的分配数量和商品进行分组查询订单明细信息
     *
     * @param deliveryList 订单列表
     * @return
     */
    private List<Map<String, Object>> getDeliveryInfoByGroup(List<Delivery> deliveryList) {
        List<String> deliveryCodes = deliveryList.stream().map(e -> e.getCode()).collect(Collectors.toList());
        return iDeliveryItemService.getDeliveryItemList(deliveryCodes);
    }

    /**
     * 构建要生成波次的条件信息
     *
     * @param temporaryCode 临时波次标识
     * @param temp          订单分组信息
     * @return
     */
    private List<Map<String, Object>> getMaps(String temporaryCode, List<Map<String, Object>> temp) {
        List<Map<String, Object>> mapList;
        mapList = temp.stream().map(m -> {
            Map<String, Object> tmp = new HashMap<>();
            tmp.put(SHIPMENT_CODE, MapUtil.getStr(m, SHIPMENT_CODE));
            tmp.put(LOGISTICS_CODE, MapUtil.getStr(m, LOGISTICS_CODE));
            tmp.put(PREPACKAGEDCODE, MapUtil.getStr(m, PREPACKAGEDCODE));
            tmp.put(TEMPORARY_CODE, temporaryCode);
            return tmp;
        }).collect(Collectors.toList());
        return mapList;
    }


    /**
     * 订单处理页面-商品查询总数
     *
     * @param waveQueryGoodsDTO
     * @return
     */
    @Override
    public WaveQueryGoodsVO waveQueryGoodsSum(WaveQueryGoodsDTO waveQueryGoodsDTO) {
        waveQueryGoodsDTO.setGroupCode(UserUtil.getBranchCode());
        return this.baseMapper.waveQueryGoodsSum(waveQueryGoodsDTO);
    }

    /**
     * 订单处理页面-商品查询结果
     *
     * @param waveQueryGoodsDTO
     * @return
     */
    @Override
    public List<WaveQueryGoodsVO> waveQueryGoods(WaveQueryGoodsDTO waveQueryGoodsDTO) {
        waveQueryGoodsDTO.setGroupCode(UserUtil.getBranchCode());
        return this.baseMapper.waveQueryGoods(waveQueryGoodsDTO);
    }


    /**
     * 订单处理页面-商品查询总数
     *
     * @param waveQueryGoodsDTO
     * @return
     */
    @Override
    public WaveQueryGoodsVO waveQueryHotGoodsSum(WaveQueryGoodsDTO waveQueryGoodsDTO) {
        waveQueryGoodsDTO.setGroupCode(UserUtil.getBranchCode());
        return this.baseMapper.waveQueryHotGoodsSum(waveQueryGoodsDTO);
    }

    /**
     * 订单处理页面-商品查询结果
     *
     * @param waveQueryGoodsDTO
     * @return
     */
    @Override
    public List<WaveQueryGoodsVO> waveQueryHotGoods(WaveQueryGoodsDTO waveQueryGoodsDTO) {
        waveQueryGoodsDTO.setGroupCode(UserUtil.getBranchCode());
        return this.baseMapper.waveQueryHotGoods(waveQueryGoodsDTO);
    }

    /**
     * 当订单不需要复核操作时，需要在生成出库单时更新订单状态
     *
     * @param wavePickingCode
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateStatusWithoutRecheck(String wavePickingCode) {
        return this.baseMapper.updateStatusWithoutRecheck(wavePickingCode,UserUtil.getBranchCode());
    }

    /**
     * 订单需要复核操作时，需要再生成出库单时更新订单状态
     *
     * @param wavePickingCode
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateStatusWithRecheck(String wavePickingCode,String groupCode) {
        return this.baseMapper.updateStatusWithRecheck(wavePickingCode,groupCode);
    }

    /**
     * 更新订单状态和数量
     *
     * @param wavePickingCode
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateStatusAndQuantity(String wavePickingCode) {
        //更新订单数量
        this.updatePickingQuantityByWavePickingCode(wavePickingCode,UserUtil.getBranchCode());
        //更新订单状态
        return this.update(new UpdateWrapper<Delivery>().lambda().eq(Delivery::getWavePickingCode, wavePickingCode).eq(Delivery::getGroupCode, UserUtil.getBranchCode())
                .apply("total_num=pick_num + pick_stock_out_num").set(Delivery::getStatus, DeliveryStatusEnum.UN_RECHECK.getValue()));
    }

    /**
     * 更新复核数量
     *
     * @param deliveryCode
     */
    @Override
    public int updateRecheckQuantity(String deliveryCode) {
        return this.baseMapper.updateRecheckQuantity(deliveryCode,UserUtil.getBranchCode());
    }

    /**
     * 根据订单的计划数量与（订单的拣货数量和缺货数量之间的关系）更新订单状态
     *
     * @param delivery
     * @return
     */
    @Override
    public void updateDelvieryStatus(Delivery delivery) {
        //totalQuantity=拣货数量+拣货缺货数量
        BigDecimal totalQuantity = delivery.getPickNum().add(delivery.getPickStockOutNum());
        String deliveryCode = delivery.getCode();
        //0<拣货数量+拣货缺货数量=总数量时，订单状态为拣货完成
        if (delivery.getTotalNum().compareTo(totalQuantity) == NUM_ZERO) {
            this.update(new UpdateWrapper<Delivery>().lambda().eq(Delivery::getCode, deliveryCode).eq(Delivery::getGroupCode,UserUtil.getBranchCode()).set(Delivery::getStatus, DeliveryStatusEnum.PICKED.getValue()));
        }
        //0<拣货数量+拣货缺货数量<总数量时，订单状态为拣货中
        if (delivery.getTotalNum().compareTo(totalQuantity) > NUM_ZERO && totalQuantity.compareTo(BigDecimal.ZERO) > NUM_ZERO) {
            this.update(new UpdateWrapper<Delivery>().lambda().eq(Delivery::getCode, deliveryCode).eq(Delivery::getGroupCode,UserUtil.getBranchCode()).set(Delivery::getStatus, DeliveryStatusEnum.PICKING.getValue()));
        }
    }

    /**
     * 订单清空运单号
     *
     * @param deliveryCode
     * @author liuxr
     * @date 2021/5/10 15:39
     */
    @Override
    public void waybillDiscard(String deliveryCode) {
        this.baseMapper.waybillDiscard(deliveryCode,UserUtil.getBranchCode());
    }

    /**
     * 根据波次号和订单状态查询订单信息
     *
     * @param deliveryDTO
     * @return
     */
    @Override
    public List<DeliveryVO> queryDeliveryList(DeliveryDTO deliveryDTO) {
        deliveryDTO.setGroupCode(UserUtil.getBranchCode());
        if (StrUtil.isNotBlank(deliveryDTO.getGoodsBarCode())) {

            //根据pn获取商品code
            if(StrUtil.isNotBlank(deliveryDTO.getShipmentCode())) {
                String goodsCode =  goodsPnService.getGoodsCode(deliveryDTO.getShipmentCode(),deliveryDTO.getGoodsBarCode());
                if(StrUtil.isNotBlank(goodsCode)){
                    deliveryDTO.setWhereSql(makeOrderListSubSql(goodsCode));
                }
            }
        }
        return baseMapper.queryListByGoodsBarCode(deliveryDTO);
    }


    /**
     * 查询订单列表的拼接sql
     *
     * @param goodsCode
     * @return
     */
    private String makeOrderListSubSql(String goodsCode) {
        StringBuilder sql = new StringBuilder();
        sql.append("and a.code in (").append("select b.delivery_code from wms_delivery_item b where b.goods_code = '").append(goodsCode).append("' )");
        return sql.toString();
    }

    /**
     * 根据订单code查询订单信息
     *
     * @param deliveryCode
     * @return
     */
    @Override
    public DeliveryVO getDeliveryByCode(String deliveryCode, String status, String goodsBarCode) {
        DeliveryVO deliveryVO = getVOByCode(deliveryCode);
        List<DeliveryItemWithDistributionVO> deliveryItemWithDistributionVoS = deliveryVO.getDeliveryItemWithDistributionVOList();
        if (StrUtil.isBlank(goodsBarCode)) {
            deliveryItemWithDistributionVoS = deliveryItemWithDistributionVoS.stream().filter(e -> e.getItemStatus().equals(status)).collect(Collectors.toList());
        } else {

                //根据pn获取商品code
                if(StrUtil.isNotBlank(deliveryVO.getShipmentCode())) {
                    String goodsCode =  goodsPnService.getGoodsCode(deliveryVO.getShipmentCode(),goodsBarCode);
                    if(StrUtil.isNotBlank(goodsCode)){
                        deliveryItemWithDistributionVoS = deliveryItemWithDistributionVoS.stream().filter(e -> e.getItemStatus().equals(status) && e.getGoodsCode().equals(goodsCode)).collect(Collectors.toList());
                    }else{
                        deliveryItemWithDistributionVoS = deliveryItemWithDistributionVoS.stream().filter(e -> e.getItemStatus().equals(status) && e.getGoodsBarCode().equals(goodsBarCode)).collect(Collectors.toList());
                    }
                }

        }


        deliveryVO.setDeliveryItemWithDistributionVOList(deliveryItemWithDistributionVoS);
        return deliveryVO;
    }

    /**
     * @param itemCode :
     * @param status   :
     * @description 根据拣货量与复核量+退减量的比较，尝试更新明细的状态
     * @author xuyang
     * @date 2020/10/29 8:55
     **/
    @Override
    public int updateStatusIfFinishRecheck(String itemCode, String status) {
        return this.baseMapper.updateStatusIfFinishRecheck(itemCode, status,UserUtil.getBranchCode());
    }

    /**
     * 根据波次号统计订单计划总量
     *
     * @param wavePickingCode 波次号
     * @return java.math.BigDecimal 订单计划总量
     * @author liuxr
     * @date 2021/5/15 9:56
     */
    @Override
    public BigDecimal getTotalNumByWavePickingCode(String wavePickingCode) {
        return this.baseMapper.getTotalNumByWavePickingCode(wavePickingCode,UserUtil.getBranchCode());
    }

    /**
     * 根据波次号统计订单商品种类总量
     *
     * @param wavePickingCode 波次号
     * @return java.math.BigDecimal  订单商品种类总量
     * @author liuxr
     * @date 2021/5/15 9:56
     */
    @Override
    public BigDecimal getGoodsTotalNumByWavePickingCode(String wavePickingCode) {
        return this.baseMapper.getGoodsTotalNumByWavePickingCode(wavePickingCode,UserUtil.getBranchCode());
    }

    /**
     * 根据波次号更新发货明细的复核数量
     *
     * @param wavePickingCode
     * @return
     */
    @Override
    public int updateRecheckQuantityByWavePickingCode(String wavePickingCode) {
        return this.baseMapper.updateRecheckQuantityByWavePickingCode(wavePickingCode,UserUtil.getBranchCode());
    }

    /**
     * 更新复核数量和状态
     *
     * @param deliveryCodes
     * @param status
     */
    @Override
    public void updateRecheckQuantityAndStatus(Set<String> deliveryCodes, String status) {

        this.baseMapper.updateRecheckQuantityAndStatus(deliveryCodes, status,UserUtil.getBranchCode());
    }



    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePrintCount(String deliveryCode) {

        this.baseMapper.addPrintCount(deliveryCode,UserUtil.getBranchCode());
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchUpdatePrintCount(List<DeliveryDTO> deliveryDTOS) {
        String branchCode = UserUtil.getBranchCode();
        deliveryDTOS.forEach(e->{
            e.setGroupCode(branchCode);
        });
        if(ObjectUtil.isNotEmpty(deliveryDTOS) && StringUtils.isNotEmpty(deliveryDTOS.get(0).getWavePickingCode())){
            //更新拣货单
            String status = "1";
            for (DeliveryDTO e : deliveryDTOS) {
                if(e.getWaybillPrintCount() == 0){
                    status = "0";
                    break;
                }
            }
            if(status.equals("1")){
                iPickingService.updatePrintCountV(deliveryDTOS.get(0).getWavePickingCode(),branchCode);
            }
        }
        this.baseMapper.batchUpdatePrintCount(deliveryDTOS);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchUpdatePrintCount(List<String> codes,Integer printNum) {
        String groupCode = UserUtil.getBranchCode();
        this.update(new LambdaUpdateWrapper<Delivery>()
                .in(Delivery::getCode, codes)
                .eq(Delivery::getGroupCode,groupCode)
                .setSql("waybill_print_count = waybill_print_count +" + printNum)
        );
    }

    /**
     * 查询合计,异常订单总数
     *
     * @param deliveryDTO
     * @return
     */
    @Override
    public DeliveryVO queryAbnormalListSum(DeliveryDTO deliveryDTO) {
        return this.baseMapper.queryAbnormalListSum(deliveryDTO);
    }

    /**
     * 查询异常单列表
     *
     * @param deliveryDTO
     * @return
     */
    @Override
    public List<DeliveryVO> queryAbnormalList(DeliveryDTO deliveryDTO) {
        //排序字段名需要驼峰转数据库下划线类型字段名
        if (StringUtils.isNotEmpty(deliveryDTO.getSortField())) {
            deliveryDTO.setSortField(CommonUtil.camel2Underline(deliveryDTO.getSortField()));
        }

        List<DeliveryVO> deliveryVOS = baseMapper.queryAbnormalList(deliveryDTO);
        return deliveryVOS.stream().peek(info -> {
            DeliveryStatusEnum enumByCode = DeliveryStatusEnum.getEnumByValue(info.getStatus());
            if (null != enumByCode) {
                info.setStatusText(enumByCode.getDesc());
            }
            DeliveryTypeEnum billType = DeliveryTypeEnum.getEnumByValue(info.getType());
            if (null != billType) {
                info.setTypeText(billType.getDesc());
            }
            String logisticsName = LogisticsUtils.translate(info.getLogisticsCode());
            if (StrUtil.isEmpty(logisticsName)) {
                info.setLogisticsName(logisticsName);
            }
            String carrierTypeName = CarrierTypeEnum.translate(info.getCarrierType());
            if (StrUtil.isNotEmpty(carrierTypeName)) {
                info.setCarrierTypeName(carrierTypeName);
            }
            // 翻译订单来源类型
            info.setSourceTypeText(DeliverySourceTypeEnum.translate(info.getSourceType()));
            //翻译订单来源平台名称
            info.setSourcePlatformName(SourcePlatformUtils.translate(info.getSourcePlatformCode()));

            info.setWarehouseName(warehouseCache.translate(info.getWarehouseCode()));
        }).collect(Collectors.toList());
    }

    /**
     * 波次拣货确认，不需要复核操作时，自动更新订单复核数量及状态
     *
     * @param wavePickingCode 波次号
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void autoUpdateStatusAndRecheckQuantity(String wavePickingCode,String groupCode) {
        this.baseMapper.autoUpdateStatusAndRecheckQuantity(wavePickingCode,groupCode);
    }

    /**
     * 复核出库确认，自动更新订单复核数量及状态
     * @param deliveryDistributionDTO 订单号集合
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void autoUpdateRecheckQuantityByCodes(DeliveryDistributionDTO deliveryDistributionDTO){
        this.baseMapper.autoUpdateRecheckQuantityByCodes(deliveryDistributionDTO);
    }

    /**
     * 波次拣货确认，不需要复核操作时，自动更新订单出库数量及状态
     *
     * @param deliveryCodes 订单号
     * @param groupCode 订单号
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void autoUpdateStatusAndSendQuantity(Set<String> deliveryCodes,String groupCode) {
        log.info("自动更新订单出库数量及状态,{}",JSONUtil.toJsonStr(deliveryCodes));
        this.baseMapper.autoUpdateStatusAndSendQuantity(deliveryCodes,groupCode);
    }

    /**
     * 波次拣货缺货登记，更新订单缺货数量
     *
     * @param deliveryCode 订单号
     * @author liuxr
     * @date 2021/6/5 17:05
     */
    @Override
    public void updateOutStockQuantity(String deliveryCode) {

        this.baseMapper.updateOutStockQuantity(deliveryCode,UserUtil.getBranchCode());
    }

    /**
     * 波次拣货缺货登记，更新订单分配明细、订单明细缺货数量、拣货数量和状态
     *
     * @param refundQuantity           缺货数量
     * @param deliveryDistributionCode 分配明细标识
     * @param deliveryItemCode         订单明细标识
     * @author liuxr
     * @date 2021/6/5 17:05
     */
    @Override
    public void updateItemsOutStockQuantity(BigDecimal refundQuantity, String deliveryDistributionCode, String deliveryItemCode) {
        this.baseMapper.updateItemsOutStockQuantity(refundQuantity, deliveryDistributionCode, deliveryItemCode);
    }

    /**
     * 出库订单数量统计 Visual
     *
     * @return
     */
    @Override
    public String outboundQuantityTrend(String time, String status) {
        return baseMapper.outboundQuantityTrend(time, status,UserUtil.getBranchCode());
    }

    /**
     * 根据波次单号修改发货单状态
     *
     * @param deliveryCode
     * @param status
     */
    @Override
    public void updateStatusByCode(String deliveryCode, String status,String groupCode) {
        this.update(new LambdaUpdateWrapper<Delivery>().eq(Delivery::getCode, deliveryCode)
                .set(Delivery::getStatus, status).eq(Delivery::getGroupCode,groupCode));
    }

    /**
     * 查询合计,待打包订单总数
     *
     * @param deliveryDTO
     * @return
     */
    @Override
    public DeliveryVO queryWaitingPackingOrderListSum(DeliveryDTO deliveryDTO) {
        deliveryDTO.setGroupCode(UserUtil.getBranchCode());
        return this.baseMapper.queryWaitingPackingOrderListSum(deliveryDTO);
    }



    /**
     * 根据codes更新实体
     * @param dto
     * @return
     */
    @Override
    public int updateByCodes(DeliveryDTO dto) {
        dto.setModifiedBy(UserUtil.getCode());
        dto.setModifiedName(partyCache.translate(UserUtil.getCode()));
        return this.baseMapper.updateByCodes(dto);
    }

    /**
     * 发货单打包任务分组信息
     *  [shipmentCode:,carrierCode:,count:,totalNum:]
     * @param codes 订单号
     * @return
     */
    @Override
    public List<Map<String,Object>> queryPackTaskGroupInfo(List<String> codes){
        return this.baseMapper.queryPackTaskGroupInfo(codes,UserUtil.getBranchCode());
    }

    /**
     * 根据波次获取预打包订单 列表
     *
     * @param wavePickingCode 波次号
     * @return
     */
    @Override
    public List<DeliveryVO> queryDeliveryListByWavingCode(String wavePickingCode){
        List<Delivery> deliveryList = this.list(new LambdaQueryWrapper<Delivery>()
                .select(Delivery::getCode, Delivery::getOrderNum, Delivery::getReceiverName,Delivery::getPickNum)
                .eq(Delivery::getWavePickingCode, wavePickingCode)
                .eq(Delivery::getGroupCode,UserUtil.getBranchCode()));
        //根据orderNum重排序
        //如果订单生成波次，则根据订单排序
        long count = deliveryList.stream().filter(item -> ObjectUtil.isNotEmpty(item.getOrderNum())).count();
        if (count == deliveryList.size()) {
            deliveryList = deliveryList.stream().sorted(Comparator.comparing(Delivery::getOrderNum)).collect(toList());
        }
        List<DeliveryVO> deliveryVOList = deliveryList.stream().map(info -> {
            DeliveryVO deliveryVO = new DeliveryVO();
            BeanUtil.copyProperties(info, deliveryVO);
            return deliveryVO;
        }).collect(Collectors.toList());
        return deliveryVOList;
    }

    /**
     * 根据订单号更新发货单的复核数量
     * @param code
     * @return
     */
    @Override
    public int updateRecheckQuantityByCode(String code,String groupCode) {
        return this.baseMapper.updateRecheckQuantityByCode(code,groupCode);
    }

    /**
     * @description
     * 根据波次号，更新复合数量和状态
     * @author rfwang
     * @date 2021年7月16日
     * @param wavePickingCode:
     **/
    @Override
    public int updateRecheckQuantityAndStatsuByWavePickingCode(String wavePickingCode){
        return this.baseMapper.updateRecheckQuantityAndStatsuByWavePickingCode(wavePickingCode, UserUtil.getBranchCode());
    }


    /**
     * @param dbSale
     * @description 根据oms销售单生成wms的发货单
     * @author xuyang
     * @date 2020/11/9 9:44
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Delivery createByOmsSale(Sale dbSale) {
        String createdCode = UserUtil.getCode();
        String createdName = partyCache.translate(createdCode);
        LocalDateTime currentDateTime = LocalDateTime.now();
        // 获取销售单明细
        List<SaleItem> saleItems = dbSale.getSaleItemList();
        // 生成发货单
        Delivery delivery = new Delivery();
        // 不使用 BeanUtil.copyProperties(sale, delivery); 因为status字段虽然一样，但类型不一样，会报错
        Warehouse warehouse = warehouseCache.getObj(dbSale.getWarehouseCode());
        if (warehouse != null) {
            delivery.setWarehouseSerialNumber(warehouse.getSerialNumber());
        }
//        Carrier carrier = carrierCache.getObj(dbSale.getCarrierCode());
        //根据快递名称获取快递信息
        Carrier carrier = carrierService.getOne(new LambdaQueryWrapper<Carrier>()
                .eq(Carrier::getShipmentCode, dbSale.getShipmentCode())
                .eq(Carrier::getDeleted, 0)
                .eq(Carrier::getCarrierType, CarrierTypeEnum.EXPRESS.getValue())
                .eq(Carrier::getName, dbSale.getCarrierName())
                .eq(Carrier::getStatus, "1"));
        if (carrier != null) {
            delivery.setLogisticsCode(carrier.getOuterCode());
            delivery.setSenderMobile(carrier.getSenderMobile());
            delivery.setSenderName(carrier.getSenderName());
            delivery.setSenderProvince(carrier.getProvince());
            delivery.setSenderCity(carrier.getCity());
            delivery.setSenderDistrict(carrier.getCounty());
            delivery.setSenderDetailAddress(carrier.getAddress());
            delivery.setCarrierCode(carrier.getCode());
            delivery.setCarrierName(carrier.getName());
            delivery.setCarrierType(carrier.getCarrierType());
            delivery.setTemplateUrl(carrier.getStandardTemplateUrl());
            delivery.setSourcePlatformCode(carrier.getPlatformType());
        }
        delivery.setGoodsSortQuantity(BigDecimal.valueOf(dbSale.getSaleItemList().size()));
        if (delivery.getGoodsSortQuantity().compareTo(BigDecimal.ONE) > NUM_ZERO) {
            delivery.setOrderTag("多品");
        } else {
            delivery.setOrderTag("单品");
        }
        delivery.setReceiverName(dbSale.getBuyerName());
        delivery.setReceiverMobile(dbSale.getBuyerMobile());
        delivery.setReceiverProvince(dbSale.getProvinceName());
        delivery.setReceiverCity(dbSale.getCityName());
        delivery.setReceiverDistrict(dbSale.getCountyName());
        delivery.setReceiverDetailAddress(dbSale.getBuyerAddress());
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        delivery.setOrderType(dbSale.getSaleType());
        delivery.setPlaceOrderTime(dbSale.getThirdOrderTime() != null ? df.format(dbSale.getThirdOrderTime()) : "");
        delivery.setShopNick(dbSale.getShopName());
        delivery.setWarehouseCode(dbSale.getWarehouseCode());
        delivery.setShipmentCode(dbSale.getShipmentCode());
        delivery.setShipmentName(dbSale.getShipmentName());

        delivery.setGroupCode(dbSale.getGroupCode());
        delivery.setCustomerCode(dbSale.getCustomerCode());
        delivery.setType(DeliveryTypeEnum.OUT_SALE.getValue());

        // 生成发货单单号
        String deliveryCode = OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.FH, dbSale.getShipmentCode(),UserUtil.getBranchCode());
        delivery.setCode(deliveryCode);
        delivery.setStatus(DeliveryStatusEnum.UN_DISTRIBUTION.getValue());
        delivery.setSourceCode(dbSale.getCode());
        delivery.setDistributionNum(BigDecimal.ZERO);
        delivery.setPickNum(BigDecimal.ZERO);
        delivery.setSendNum(BigDecimal.ZERO);
        delivery.setRemarks(StrUtil.EMPTY);
        delivery.setCreatedBy(createdCode);
        delivery.setCreatedName(createdName);
        delivery.setGmtCreated(currentDateTime);
        delivery.setModifiedBy(StrUtil.EMPTY);
        delivery.setModifiedName(StrUtil.EMPTY);
        delivery.setGmtModified(null);
        delivery.setTotalNum(dbSale.getPlanQuantity());
        delivery.setSourceType(DeliverySourceTypeEnum.OMS.getValue());
        // 标记为oms下发的单据
        delivery.setIsOmsOrder(NUM_ONE);

        // 保存发货单
        save(delivery);

        // 生成明细
        List<DeliveryItem> deliveryItems = saleItems.stream().map(e->{
            DeliveryItem deliveryItem = new DeliveryItem();
            BeanUtil.copyProperties(e, deliveryItem);
            deliveryItem.setCode(IdUtil.simpleUUID());
            deliveryItem.setDeliveryCode(deliveryCode);
            deliveryItem.setSaleItemCode(e.getCode());
            deliveryItem.setSourceItemCode(e.getCode());
            deliveryItem.setCreatedBy(createdCode);
            deliveryItem.setCreatedName(createdName);
            deliveryItem.setGmtCreated(currentDateTime);
            deliveryItem.setGoodsUnit(e.getSaleUnit());
            deliveryItem.setGoodsUnitName(e.getSaleUnitName());
            deliveryItem.setQuantity(e.getPlanQuantity());
            deliveryItem.setBasicQuantity(e.getUnitConvertQuantity());
            deliveryItem.setTotalNum(e.getUnitConvertQuantity().multiply(e.getPlanQuantity()));
            // 清除修改人、备注等字段相同，但创建时不应该有值的字段
            deliveryItem.setRemarks(StrUtil.EMPTY);
            deliveryItem.setModifiedBy(StrUtil.EMPTY);
            deliveryItem.setModifiedName(StrUtil.EMPTY);
            deliveryItem.setGmtModified(null);

            return deliveryItem;
        }).collect(Collectors.toList());
        // 保存明细
        iDeliveryItemService.saveBatch(deliveryItems);
        return delivery;
    }

    @Override
    public void saveByOmsAllocation(OmsAllocation omsAllocation) {
        Delivery delivery = new Delivery();
        BeanUtils.copyProperties(omsAllocation, delivery);
        delivery.setCode(OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.FH, "",UserUtil.getBranchCode()));
        delivery.setWarehouseCode(omsAllocation.getSourceWarehouseCode());
        delivery.setSourceCode(omsAllocation.getCode());
        delivery.setType(DeliveryTypeEnum.ALLOCATION.getValue());
//        delivery.setDocumentType(DeliveryTypeEnum.ALLOCATION.getValue());
        delivery.setStatus(DeliveryStatusEnum.UN_DISTRIBUTION.getValue());
        delivery.setIsOmsOrder(NUM_ONE);
        delivery.setCreatedBy(UserUtil.getCode());
        delivery.setCreatedName(partyCache.translate(UserUtil.getCode()));
        delivery.setGmtCreated(LocalDateTime.now());
        delivery.setTotalNum(omsAllocation.getUnitQuantity());

        List<DeliveryItem> deliveryItemList = omsAllocation.getAllocationItemList().stream().map(item -> {
            DeliveryItem deliveryItem = new DeliveryItem();
            BeanUtils.copyProperties(item, deliveryItem);
            deliveryItem.setCode(IdUtil.simpleUUID());
            deliveryItem.setDeliveryCode(delivery.getCode());
            deliveryItem.setSourceItemCode(item.getCode());
            deliveryItem.setTotalNum(item.getQuantity());
            deliveryItem.setQuantity(item.getUnitQuantity());
            deliveryItem.setCreatedBy(UserUtil.getCode());
            deliveryItem.setCreatedName(partyCache.translate(UserUtil.getCode()));
            deliveryItem.setGmtCreated(LocalDateTime.now());

            deliveryItem.setGoodsUnit(item.getUnitCode());
            deliveryItem.setGoodsUnitName(item.getUnitName());
            deliveryItem.setBasicQuantity(item.getUnitConvertQuantity());
            deliveryItem.setBasicUnit(item.getBasicUnit());
            deliveryItem.setBasicUnitName(item.getBasicUnitName());

            return deliveryItem;
        }).collect(Collectors.toList());

        this.save(delivery);
        iDeliveryItemService.saveBatch(deliveryItemList);
    }

    /**
     * @description
     * 订单发货确认
     * @author xuyang
     * @date 2021/8/25 13:13
     * @param sourceCode:
     **/
    @Override
    public Result deliveryConfirm(String sourceCode,String wavePickingCode){
        //如果是按单，就不走发货确认
        Picking picking = iPickingService.getOne(new LambdaQueryWrapper<Picking>()
                .eq(Picking::getWavePickingCode, wavePickingCode)
                .eq(Picking::getGroupCode,UserUtil.getBranchCode()));
        if (OrderTagEnum.QUANTITY.getValue().equals(picking.getOrderTag())) {
            return Result.ok();
        }
        //判断是否是合单
        if (sourceCode.startsWith(OrderIdGeneratorUtil.OrderTypeEnum.MG.getCode())) {
            return mergeDeliveryConfirm(sourceCode);
        } else {
            return ordinaryDeliveryConfirm(sourceCode);
        }
    }

    private Result ordinaryDeliveryConfirm (String sourceCode) {
        Sale sale = iSaleService.getOne(new LambdaQueryWrapper<Sale>()
                .eq(Sale::getCode, sourceCode));
        if (sale == null) {
            return Result.error("销售单不能能为空");
        }
        //普通销售单，返回成功
        if(SaleTypeEnum.NORMAL.getValue().equals(sale.getSaleType())) {
            return Result.ok();
        }

        if (SaleTypeEnum.FROM_PDD.getValue().equals(sale.getSaleType())) {
            return pddOrderConfirm(sale);
        }

        if (SaleTypeEnum.FROM_KS.getValue().equals(sale.getSaleType())) {
            return ksOrderConfirm(sale);
        }

        if (SaleTypeEnum.FROM_TB.getValue().equals(sale.getSaleType())) {
            return tbOrderConfirm(sale);
        }
        return Result.ok();
    }

    private Result mergeDeliveryConfirm (String sourceCode){
        List<Sale> saleList = saleService.list(new QueryWrapper<Sale>().lambda().eq(Sale::getMergeCode, sourceCode));
        if (IterUtil.isNotEmpty(saleList)) {
            List<Result> results = Lists.newArrayList();
            for (Sale sale : saleList) {
                //普通销售单，返回成功
                if(SaleTypeEnum.NORMAL.getValue().equals(sale.getSaleType())) {
                    results.add(Result.ok());
                }

                if (SaleTypeEnum.FROM_PDD.getValue().equals(sale.getSaleType())) {
                    results.add(pddOrderConfirm(sale));
                }

                if (SaleTypeEnum.FROM_KS.getValue().equals(sale.getSaleType())) {
                    results.add(ksOrderConfirm(sale));
                }

                if (SaleTypeEnum.FROM_TB.getValue().equals(sale.getSaleType())) {
                    results.add(tbOrderConfirm(sale));
                }
            }
            return returnByStatus(results);
        }
        return Result.ok();
    }

    private Result returnByStatus (List<Result> results) {
        //合单中如果订单确认有一个没有确认成功，返回失败
       for (Result result : results) {
           if (!((int)result.get(CommonConst.STATUS) == 0)) {
               return Result.error();
           }
       }
        return Result.ok();
    }
    private Result pddOrderConfirm(Sale sale) {
        //店铺为空时及获取不到登录token时，都需要返回false
        if (StrUtil.isEmpty(sale.getShopCode())) {
            return Result.error("拼多多店铺code不能为空");
        }
        Shop shop = shopService.getOne(new LambdaQueryWrapper<Shop>().eq(Shop::getCode, sale.getShopCode()));
        if (shop == null) {
            return Result.error("拼多多订单确认，店铺信息不能为空");
        }
        String accessToken = shop.getAccessToken();
        if (StrUtil.isEmpty(accessToken)) {
            return Result.error("拼多多店铺表的accessToken不能为空");
        }
        PddOrderDto dto = new PddOrderDto();
        dto.setLogisticsId(sale.getLogisticsId());
        dto.setOrderSn(sale.getSourceCode());
        dto.setTrackingNumber(sale.getTrackingNumber());
        dto.setAccessToken(accessToken);
        String jsonStr = JSONUtil.toJsonStr(dto);
        log.info("订单发货确认url:{},入参：{}",pddSendUrl,jsonStr);
        String responseJson = HttpUtil.post(pddSendUrl, jsonStr);
        log.info("订单发货确认返回时间：{}，返回结果：{}",LocalDateTime.now(),responseJson);
        Result result = JSONUtil.toBean(responseJson, Result.class);
        log.info("返回结果实体类为：{}" , JSONUtil.toJsonStr(result));
        return result;
    }

    private Result ksOrderConfirm (Sale sale) {
        //店铺为空时及获取不到登录token时，都需要返回false
        if (StrUtil.isEmpty(sale.getShopCode())) {
            return Result.error("快手店铺code不能为空");
        }
        Shop shop = shopService.getOne(new LambdaQueryWrapper<Shop>().eq(Shop::getCode, sale.getShopCode()));
        if (shop == null) {
            return Result.error("快手订单确认，店铺信息不能为空");
        }
        String accessToken = shop.getAccessToken();
        if (StrUtil.isEmpty(accessToken)) {
            return Result.error("快手店铺表的accessToken不能为空");
        }
        KsOrderDto dto = new KsOrderDto();
        dto.setExpressNo(sale.getWaybillCode());
        dto.setOrderId(Long.parseLong(sale.getSourceCode()));
        KuaishouCarrierEnum carrierEnum = KuaishouCarrierEnum.getEnumByValue(sale.getCarrierName());
        if (carrierEnum == null) {
            return Result.error("快手承运商信息为空或者不正确，请确认");
        }
        dto.setExpressCode(Integer.parseInt(carrierEnum.getDesc()));
        dto.setAccessToken(accessToken);
        String jsonStr = JSONUtil.toJsonStr(dto);
        log.info("订单发货确认url:{},入参：{}",ksSendUrl,jsonStr);
        String responseJson = HttpUtil.post(ksSendUrl, jsonStr);
        log.info("订单发货确认返回时间：{}，返回结果：{}",LocalDateTime.now(),responseJson);
        Result result = JSONUtil.toBean(responseJson, Result.class);
        return result;
    }

    private Result tbOrderConfirm(Sale sale) {
        //店铺为空时及获取不到登录token时，都需要返回false
        if (StrUtil.isEmpty(sale.getShopCode())) {
            return Result.error("淘宝店铺code不能为空");
        }
        Shop shop = shopService.getOne(new LambdaQueryWrapper<Shop>().eq(Shop::getCode, sale.getShopCode()));
        if (shop == null) {
            return Result.error("淘宝订单确认，店铺信息不能为空");
        }
        TbOrderDto dto = new TbOrderDto();
        dto.setCompanyCode(sale.getCarrierCode());
        dto.setOutSid(sale.getWaybillCode());
        dto.setFlag(StrUtil.EMPTY);
        dto.setMemo(StrUtil.EMPTY);
        dto.setShopNick(shop.getName());
        dto.setShopUrl(shop.getAccessAddress());
        dto.setTid(Integer.parseInt(sale.getSourceCode()));
        String jsonStr = JSONUtil.toJsonStr(dto);
        log.info("订单发货确认url:{},入参：{}",tbSendUrl,jsonStr);
        String responseJson = HttpUtil.post(tbSendUrl, jsonStr);
        log.info("订单发货确认返回时间：{}，返回结果：{}",LocalDateTime.now(),responseJson);
        Result result = JSONUtil.toBean(responseJson, Result.class);
        return result;
    }

    /**
     * @description  更新作废单状态
     * @author rfwang
     * @date 2021/11/3 18:36
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void updateCanceledDeliveryStatus(String groupCode,List<String> warehouseCodeList,List<String> shipmentCodeList){
        //更新订单状态
        List<Delivery> deliveries = list(Wrappers.lambdaQuery(Delivery.class)
                .select(Delivery::getCode)
                .in(Delivery::getWarehouseCode, warehouseCodeList)
                .eq(Delivery::getGroupCode,groupCode)
                .eq(Delivery::getIsCancel, NumConst.NUM_ONE)
                .ne(Delivery::getStatus, DeliveryStatusEnum.VOID.getValue()));
        log.info("获取待取消订单列表：{}",JSONUtil.toJsonStr(deliveries));
        if (IterUtil.isNotEmpty(deliveries)) {
            List<String> codes = deliveries.stream().map(Delivery::getCode).collect(Collectors.toList());
            orderCancelService.deliveryOrderCancel(codes);
        }
    }

    /**
     * 查询当前波次下的订单数量
     * @param wavePickingCode
     * @return
     */
    @Override
    public int cancelWaveTipsPrompt(String wavePickingCode) {
        Integer count = 0;
        try {
            count = baseMapper.cancelWaveTipsPrompt(wavePickingCode,UserUtil.getBranchCode());
        } catch (Exception e){
            e.printStackTrace();
            log.info("波次查询订单数量异常" + e.getMessage());
        }
        return count;
    }

    /**
     * 根据波次号更新面单的打印次数
     * @param wavePickingCode
     * @return
     */
    @Override
    public void updatePrintCountByWavePickingCode(String wavePickingCode,String groupCode){
        this.baseMapper.updatePrintCountByWavePickingCode(wavePickingCode,groupCode);
    }

    /**
     * 更爱承运商
     * @param deliveryCodes
     */
    @Override
    public void checkDelivery(List<String> deliveryCodes,String carrierCode) {
        String branchCode = UserUtil.getBranchCode();
        Carrier carriers = carrierCache.getCarrier(carrierCode);
        //含有承运商的订单codes集合
        Set<String> hasCarrierCodeDelvieryCodes = new HashSet<>();
        //没有承运商的非京东平台的订单code集合
        Set<String> notJDEmptyCarrierCodeDeliveryCodes = new HashSet<>();
        if (null == carriers) {
            log.error("找不到承运商信息，无法获取面单号。");
            throw new ServiceException("找不到承运商信息");
        }

        String shopCode = carriers.getShopCode();
        if(StrUtil.isBlank(shopCode)){
            log.error("找不到承运商对应的店铺信息，无法获取面单号。");
            throw new ServiceException("找不到承运商对应的店铺信息");
        }

        Shop shop = shopCache.getShop(shopCode);
        if(null==shop){
            log.error("找不到承运商对应的店铺信息，无法获取面单号。");
            throw new ServiceException("找不到承运商对应的店铺信息");
        }

        List<Delivery> deliveryList = this.list(new LambdaQueryWrapper<Delivery>()
                .select(Delivery::getId,
                        Delivery::getCode,
                        Delivery::getCarrierType,
                        Delivery::getGroupCode,
                        Delivery::getWaybillCode,
                        Delivery::getSplitWaybillCode,
                        Delivery::getShipmentCode,
                        Delivery::getCarrierCode,
                        Delivery::getStatus,
                        Delivery::getIsCancel
                )
                .in(Delivery::getCode, deliveryCodes)
                .eq(Delivery::getGroupCode, branchCode));
        List<Delivery> carrierCodeEmptyDeliverys = deliveryList.stream().filter(e -> StrUtil.isEmpty(e.getCarrierCode())).collect(Collectors.toList());
        List<Delivery> hasCarrierCodeDeliverys = deliveryList.stream().filter(e -> StrUtil.isNotEmpty(e.getCarrierCode())).collect(Collectors.toList());
       if (IterUtil.isNotEmpty(hasCarrierCodeDeliverys)) {
           // 校验是否都是同一承运商
           int logisticsSize = hasCarrierCodeDeliverys.stream().collect(Collectors.groupingBy(Delivery::getCarrierCode)).size();
           if (logisticsSize > NUM_ONE) {
               throw new ServiceException(MUST + CHOOSE + SAME + LOGISTICS_NAME + DELIVERY);
           }
           // 校验是否都是同一货主
           int shipmentSize = hasCarrierCodeDeliverys.stream().collect(Collectors.groupingBy(Delivery::getShipmentCode)).size();
           if (shipmentSize > NUM_ONE) {
               throw new ServiceException(MUST + CHOOSE + SAME + SHIPMENT_NAME + DELIVERY);
           }

           if(!carriers.getShipmentCode().equalsIgnoreCase(hasCarrierCodeDeliverys.get(0).getShipmentCode())){
               throw new ServiceException("更改的承运商和所选订单的承运商不属于同一个入驻商!");
           }

           Carrier oldCarriers = carrierCache.getCarrier(hasCarrierCodeDeliverys.get(0).getCarrierCode());
           if(ObjectUtil.isNotNull(oldCarriers) && oldCarriers.getPlatformType().equals("JD")){
               throw new ServiceException("抱歉,京东平台的暂不支持更换承运商的操作!");
           }


           //校验是否包含 物流承运商
           List<Delivery> logisticsDeliveryList = hasCarrierCodeDeliverys.stream().filter(d -> StrUtil.equals(d.getCarrierType(), "logistics")).collect(toList());
           if(ObjectUtil.isNotNull(logisticsDeliveryList) && logisticsDeliveryList.size() > 0){
               throw new ServiceException("所选订单包含物流类型的订单，无法更改承运商");
           }
           //校验是否没有承运商的数据
           hasCarrierCodeDeliverys.forEach(e ->{
               //校验订单是否有承运商
               if(StringUtils.isBlank(e.getCarrierCode())){
                   throw new ServiceException("所选订单【" + e.getCode() + "】没有承运商，无法更改承运商");
               }
               //校验是否是待拣货前的订单  生成了待拣货单子就不能更改承运商了
               if(Integer.valueOf(String.valueOf(e.getStatus())) >= 6){
                   throw new ServiceException("所选订单【" + e.getCode() + "】已经生成了拣货单，无法更改承运商");
               }
           });

           //校验是否是更改前的承运商
           List<String> collect = hasCarrierCodeDeliverys.stream().map(Delivery::getCarrierCode).distinct().collect(Collectors.toList());
           if(collect.contains(carrierCode)){
               throw new ServiceException("所选数据包含更改前的承运商,请重新选择!");
           }

           hasCarrierCodeDelvieryCodes = hasCarrierCodeDeliverys.stream().map(Delivery::getCode).collect(Collectors.toSet());
       }

       if (IterUtil.isNotEmpty(carrierCodeEmptyDeliverys)) {
           List<Delivery> carrierCodeEmptyJDDeliverys = carrierCodeEmptyDeliverys.stream().filter(e -> CommonConst.JD.equals(e.getSourcePlatformCode())).collect(Collectors.toList());
           List<String> jdEmptyCarrierCodes = carrierCodeEmptyJDDeliverys.stream().map(Delivery::getCode).collect(Collectors.toList());
           if (IterUtil.isNotEmpty(jdEmptyCarrierCodes)) {
               this.updateFailureReasonOfGetWaybillCode(jdEmptyCarrierCodes, "抱歉,京东平台的暂不支持更换承运商的操作!",branchCode);
           }
           notJDEmptyCarrierCodeDeliveryCodes = carrierCodeEmptyDeliverys.stream().filter(e -> !CommonConst.JD.equals(e.getSourcePlatformCode()))
                   .collect(Collectors.toList()).stream().map(Delivery::getCode).collect(Collectors.toSet());
       }


        //已经存在面单号的去掉面单号  并  更改承运商信息
//        List<Delivery> tempDeliveryList = deliveryList.stream()
//                .filter(item -> StrUtil.isNotBlank(item.getWaybillCode())).collect(Collectors.toList());
        Set<String> collectD = new HashSet<>();
        collectD.addAll(hasCarrierCodeDelvieryCodes);
        collectD.addAll(notJDEmptyCarrierCodeDeliveryCodes);
        if(ObjectUtil.isNotNull(collectD) && collectD.size() > 0){
            boolean update = this.update(new UpdateWrapper<Delivery>().lambda()
                    .in(Delivery::getCode, collectD)
                    .eq(Delivery::getGroupCode, branchCode)
                    .set(Delivery::getWaybillCode, "")
                    .set(Delivery::getCarrierCode, carriers.getCode())
                    .set(Delivery::getCarrierName, carriers.getName())
                    .set(Delivery::getLogisticsCode, carriers.getCarrierCode())
                    .set(Delivery::getSenderName, carriers.getSenderName())
                    .set(Delivery::getSenderMobile, carriers.getSenderMobile())
                    .set(Delivery::getSenderProvince, carriers.getProvince())
                    .set(Delivery::getSenderCity, carriers.getCity())
                    .set(Delivery::getSenderDistrict, carriers.getCounty())
                    .set(Delivery::getSenderDetailAddress, carriers.getAddress())
                    .set(Delivery::getTemplateUrl, carriers.getTemplateAddress())
                    .set(Delivery::getSourcePlatformCode, carriers.getPlatformType())
                    .set(Delivery::getSplitWaybillCode, "")
                    .set(Delivery::getPackageNum, 0));
            if(update){
                //异步处理取消订单面单号
                for (Delivery delivery : deliveryList) {
                    faceBillBalanceService.getMethodResult(delivery);
                }
            }
        }
    }


    private void validateReceiveInfo(Delivery delivery) {
        if (StrUtil.isNotEmpty(delivery.getCarrierCode())) {
            if (StrUtil.isEmpty(delivery.getReceiverName())) {
                throw new ServiceException("承运商非空时，收件人不能为空");
            }
            if (StrUtil.isEmpty(delivery.getReceiverMobile())) {
                throw new ServiceException("承运商非空时，收件人手机号不能为空");
            }
            if (StrUtil.isEmpty(delivery.getReceiverProvince())) {
                throw new ServiceException("承运商非空时，收件人所在区域不能为空");
            }
            if (StrUtil.isEmpty(delivery.getReceiverDetailAddress())) {
                throw new ServiceException("承运商非空时，收件人收货地址信息不能为空");
            }
        }
    }

    /**
     * 更新获取面单失败原因
     * @param deliveryCodes
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean updateFailureReasonOfGetWaybillCode(List<String> deliveryCodes,String reason,String groupCode){
        return this.update(new LambdaUpdateWrapper<Delivery>().in(Delivery::getCode, deliveryCodes).eq(Delivery::getGroupCode, groupCode)
                .set(Delivery::getFailureReasonOfGettingWaybill,reason)
                .set(Delivery::getTaskStatus,CommonConst.STR_THREE)
        );
    }

    /**
     * 订单处理查询合计,包含总数
     * @param deliveryDTO
     * @return
     */
    @Override
    public DeliveryVO queryWaveListTotal(DeliveryDTO deliveryDTO){
        return this.baseMapper.queryWaveListTotal(deliveryDTO);
    }

    /**
     * 校验是否是同一个仓库下的订单
     * @param codes
     * @return
     */
    @Override
    public boolean validateSameWarehouseCode(List<String> codes){
       Set<String> warehouseCodes = this.list(new LambdaQueryWrapper<Delivery>()
                .select(Delivery::getWarehouseCode)
                .in(Delivery::getCode, codes)
                .eq(Delivery::getGroupCode, UserUtil.getBranchCode()))
                .stream().map(Delivery::getWarehouseCode).collect(Collectors.toSet());
       if (warehouseCodes.size() > 1) {
           throw new ServiceException("所选订单不属于同一个仓库，请确认");
       }
        return true;
    }

    /**
     * 获取积压订单数量(即未出库完成的订单数量)
     * @return
     */
    @Override
    public Result getBacklogOrderNum(){
        String groupCode = UserUtil.getBranchCode();
        List<String> warehouseCodes = UserUtil.getAuthorizeWarehouseCodes();
        List<String> shipmentCodes = UserUtil.getAuthorizeShipmentCodes();
        int num = this.count(new LambdaQueryWrapper<Delivery>()
                .in(Delivery::getWarehouseCode,warehouseCodes)
                .in(Delivery::getShipmentCode, shipmentCodes)
                .eq(Delivery::getGroupCode, groupCode)
                .eq(Delivery::getIsCancel, NUM_ZERO)
                .ne(Delivery::getStatus, DeliveryStatusEnum.DELIVERIED.getValue())
        );
        return Result.ok().data(num);
    }

    /**
     * 重新构造查询入参
     * @param deliveryDTO
     * @return
     */
    @Override
    public DeliveryDTO constructParams(DeliveryDTO deliveryDTO){
        //原始单号，来源单号，运单号，增加支持多条查询，用逗号隔开；（订单管理、订单处理、异常订单）
        if (StrUtil.isNotEmpty(deliveryDTO.getIsvCode())) {
            String[] isvCodeArr = deliveryDTO.getIsvCode().split(",");
            List<String> isvCodes = new ArrayList<>(Arrays.asList(isvCodeArr));
            deliveryDTO.setIsvCodes(isvCodes);
            deliveryDTO.setIsvCode(StrUtil.EMPTY);
        }
        if (StrUtil.isNotEmpty(deliveryDTO.getSourceCode())) {
            String[] sourceCodeArr = deliveryDTO.getSourceCode().split(",");
            List<String> sourceCodes = new ArrayList<>(Arrays.asList(sourceCodeArr));
            deliveryDTO.setSourceCodes(sourceCodes);
            deliveryDTO.setSourceCode(StrUtil.EMPTY);
        }
        if (StrUtil.isNotEmpty(deliveryDTO.getWaybillCode())) {
            String[] waybillCodeArr = deliveryDTO.getWaybillCode().split(",");
            List<String> waybillCodes = new ArrayList<>(Arrays.asList(waybillCodeArr));
            deliveryDTO.setWaybillCodes(waybillCodes);
            deliveryDTO.setWaybillCode(StrUtil.EMPTY);
        }
        //积压订单处理
        if (ObjectUtil.isNotEmpty(deliveryDTO.getBacklogOrder()) && deliveryDTO.getBacklogOrder() == 1) {
            String statusArr = DeliveryStatusEnum.ADD.getValue() + COMMA + DeliveryStatusEnum.UN_DISTRIBUTION.getValue() +
                    COMMA + DeliveryStatusEnum.PART_DISTRIBUTION.getValue() + COMMA + DeliveryStatusEnum.DISTRIBUTIONED.getValue() +
                    COMMA + DeliveryStatusEnum.UN_PICKING.getValue() + COMMA + DeliveryStatusEnum.PICKING.getValue() +
                    COMMA + DeliveryStatusEnum.PICKED.getValue() + COMMA + DeliveryStatusEnum.UN_RECHECK.getValue()+
                    COMMA + DeliveryStatusEnum.RECHECKING.getValue() + COMMA + DeliveryStatusEnum.UN_DELIVERY.getValue()
            ;
            deliveryDTO.setStatusArr(statusArr);
        }
        return deliveryDTO;
    }

    /**
     * 根据订单号或者面单号获取订单信息(即未出库完成的订单数量)
     * @return
     */
    @Override
    public Result getDeliveryInfoByOrderCode(DeliveryDTO deliveryDTO){
        //订单号或者面单号
        String requestParam = deliveryDTO.getRequestParam();
        String groupCode = deliveryDTO.getGroupCode();
        //订单状态为空时，默认查询待复核的单子
        List<String> statuses = Lists.newArrayList();
        if (DeliveryStatusEnum.UN_DELIVERY.getValue().equals(deliveryDTO.getStatus())) {
            statuses.add(DeliveryStatusEnum.UN_DELIVERY.getValue());
        } else {
            statuses.add(DeliveryStatusEnum.UN_RECHECK.getValue());
            statuses.add(DeliveryStatusEnum.RECHECKING.getValue());
        }
        if (StrUtil.isEmpty(requestParam)) {
            throw new ServiceException("入参requestParam不能为空");
        }
        Delivery delivery = null;
        if (requestParam.startsWith("FH")) {
            delivery = this.getOne(new LambdaQueryWrapper<Delivery>().eq(Delivery::getCode, requestParam).eq(Delivery::getGroupCode, groupCode));
        } else {
            List<Delivery> deliveries = this.list(new LambdaQueryWrapper<Delivery>().eq(Delivery::getWaybillCode, requestParam).eq(Delivery::getGroupCode, groupCode));
            if (deliveries.size() > 1) {
                throw new ServiceException("根据入参" + requestParam + "查询到的多条订单信息，请核实输入的面单号是否正确");
            }
            if (IterUtil.isNotEmpty(deliveries)) {
                delivery = deliveries.get(NUM_ZERO);
            }
        }
        if (null == delivery) {
            throw new ServiceException("根据入参" + requestParam + "查询到的订单信息为空，请核实输入的订单号或者面单号是否正确");
        }
        DeliveryVO deliveryVO = new DeliveryVO();
        List<DeliveryItemWithDistributionVO> deliveryItemWithDistributionVOS = Lists.newArrayList();
        BeanUtil.copyProperties(delivery,deliveryVO);
        if (StrUtil.isNotEmpty(deliveryVO.getCustomerCode())) {
            deliveryVO.setCustomerName(customerCache.translate(deliveryVO.getCustomerCode()));
        }
        //获取订单明细，相同库位下的同一商品进行合并
        List<DeliveryItem> deliveryItemList = Lists.newArrayList();
        if (StrUtil.isNotEmpty(deliveryDTO.getGoodsBarCode())) {
            deliveryItemList = iDeliveryItemService.list(new LambdaQueryWrapper<DeliveryItem>()
                    .eq(DeliveryItem::getDeliveryCode, delivery.getCode())
                    .eq(DeliveryItem::getGroupCode, groupCode)
                    .eq(DeliveryItem::getGoodsBarCode,deliveryDTO.getGoodsBarCode())
                    .in(DeliveryItem::getStatus,statuses));
        } else {
            deliveryItemList = iDeliveryItemService.list(new LambdaQueryWrapper<DeliveryItem>()
                    .eq(DeliveryItem::getDeliveryCode, delivery.getCode()).eq(DeliveryItem::getGroupCode, groupCode)
                    .in(DeliveryItem::getStatus,statuses));
        }

        if (IterUtil.isEmpty(deliveryItemList)) {
            //明细为空时，该订单状态下明细数量为0，直接返回结果
            return Result.ok().data(deliveryVO);
        }
        Map<String, List<DeliveryItem>> itemMap = deliveryItemList.stream().collect(Collectors.groupingBy(DeliveryItem::getGoodsCode));
        for (Map.Entry<String, List<DeliveryItem>> entry : itemMap.entrySet()) {
            DeliveryItemWithDistributionVO deliveryItemWithDistributionVO = new DeliveryItemWithDistributionVO();
            BeanUtil.copyProperties(entry.getValue().get(NUM_ZERO),deliveryItemWithDistributionVO);
            if (entry.getValue().size() > 1) {
                BigDecimal recheckQuantity = entry.getValue().stream().map(DeliveryItem::getRecheckQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal planQuantity = entry.getValue().stream().map(DeliveryItem::getPlanQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
                deliveryItemWithDistributionVO.setRecheckQuantity(recheckQuantity);
                deliveryItemWithDistributionVO.setPlanQuantity(planQuantity);
            }
            //添加批次规则
            Map<String, Object> goodsCacheMap = goodsCache.get(deliveryItemWithDistributionVO.getGoodsCode());
            Map<String, Object> batchRuleMap = batchRuleCache.get((String) goodsCacheMap.get(CommonConst.LOT_NUMBER));
            deliveryItemWithDistributionVO.setBatchRule(batchRuleMap);
            deliveryItemWithDistributionVOS.add(deliveryItemWithDistributionVO);
        }
        deliveryVO.setDeliveryItemWithDistributionVOList(deliveryItemWithDistributionVOS);
        return Result.ok().data(deliveryVO);
    }

    /**
     * 按照库位汇总的商品数量，更新订单的复核数量
     * @return
     */
    @Override
    public int updateDeliveryRecheckQuantity(DeliveryItemDTO deliveryItemDTO){
        String deliveryCode = deliveryItemDTO.getDeliveryCode();
        String groupCode = deliveryItemDTO.getGroupCode();
        String deliveryItemCode = deliveryItemDTO.getCode();
        BigDecimal recheckQuantity = deliveryItemDTO.getRecheckQuantity();
        iDeliveryDistributionService.updateRechekQuantityByItem(deliveryCode, deliveryItemCode, recheckQuantity, groupCode);
        iDeliveryItemService.updateRecheckQuantity(deliveryCode,groupCode,true);
        return this.updateRecheckQuantityByCode(deliveryCode,groupCode);
    }

    /**
     * 根据pn码查询商品条码
     * @author rfwang
     * @date 2022/3/18 13:13
     * @param deliveryCode 出库单号
     * @param goodsBarCode 出库明细单号
     *@param  groupCode 组织机构
     */
    @Override
    public Result getGoodsCodeByPn(String deliveryCode,String goodsBarCode,String groupCode){
        Delivery delivery = this.getOne(new LambdaQueryWrapper<Delivery>().select(Delivery::getShipmentCode)
                .eq(Delivery::getCode, deliveryCode).eq(Delivery::getGroupCode, groupCode));
        String goodsCode = goodsPnService.getGoodsCode(delivery.getShipmentCode(), goodsBarCode);
        if (StrUtil.isEmpty(goodsCode)) {
            log.error("当前订单号:【{}】没有商品pn码为【{}】的商品信息",deliveryCode,goodsBarCode);
        }
        return Result.ok().data(goodsCode);
    }

    /**
     * 查询列表
     *
     * @param deliveryDTO
     * @return
     */
    @Override
    public List<DeliveryVO> queryBdList(DeliveryDTO deliveryDTO) {
        List<DeliveryVO> deliveryVoS = baseMapper.queryBdList(deliveryDTO);
        return deliveryVoS;
    }

    /**
     * 获取订单来源平台信息
     * @param
     * @return
     */
    @Override
    public List<DeliveryVO> chooseSourcePlatformList(){
        String groupCode = UserUtil.getBranchCode();
        List<DeliveryVO> deliveryVOList = baseMapper.chooseSourcePlatformList(groupCode);
        return deliveryVOList.stream().peek(info -> {
            //翻译订单来源平台名称
            info.setSourcePlatformName(SourcePlatformUtils.translate(info.getSourcePlatformCode()) + "(" + info.getSourcePlatformCode() + ")");
        }).collect(Collectors.toList());
    }

}
